Tabellenkalkulationen sind zu einem integralen Bestandteil der modernen Computertechnologie geworden. Sie ermöglichen es Benutzern, Daten in tabellarischer Form zu organisieren, zu manipulieren und zu analysieren. Anwendungen wie Google Sheets haben den Standard für leistungsstarke, interaktive Tabellenkalkulationen gesetzt.
In diesem Blogbeitrag werden wir Sie durch den Prozess führen, eine Tabellenkalkulationsanwendung mit JavaScript zu erstellen. Wir werden uns auf wichtige Programmierkonzepte konzentrieren, JavaScript-Funktionen erkunden und detaillierte Code-Snippets mit Erklärungen einbeziehen.
Der gesamte Quellcode ist hier auf meinem Codepen verfügbar.
Was ist Google Spreadsheet?
Google Spreadsheet ist eine webbasierte Anwendung, die es Benutzern ermöglicht, online Tabellenkalkulationen zu erstellen, zu bearbeiten und zusammenzuarbeiten. Es bietet Funktionen wie Formeln, Datenvalidierung, Diagramme und bedingte Formatierung.
Unser Projekt ahmt einige Kernfunktionen von Google Spreadsheet nach, mit Schwerpunkt auf:
- Bearbeitbare Zellen.
- Formelerkennung und -auswertung.
- Live-Updates über ein Pub/Sub-Modell.
- Tastaturnavigation und Zellenauswahl.
- Dynamische Abhängigkeitsbewertung zwischen Zellen.
Funktionen dieses Projekts
- Bearbeitbare Zellen: Ermöglicht es Benutzern, Text oder Gleichungen in Zellen einzugeben.
- Formelunterstützung: Verarbeitet Formeln, die mit
=
beginnen, und bewertet Ausdrücke. - Live-Updates: Änderungen in abhängigen Zellen lösen Aktualisierungen unter Verwendung eines Pub/Sub-Modells aus.
- Tastaturnavigation: Ermöglicht die Bewegung zwischen Zellen mit den Pfeiltasten.
- Dynamische Auswertung: Stellt Echtzeitaktualisierungen für Formeln sicher, die von anderen Zellen abhängig sind.
- Fehlerbehandlung: Bietet aussagekräftige Fehlermeldungen für ungültige Eingaben oder zirkuläre Abhängigkeiten.
- Skalierbares Design: Ermöglicht eine einfache Erweiterung um weitere Zeilen, Spalten oder Funktionen.
Schlüsselkomponenten der Anwendung
1. Modusverwaltung
const Mode = {
EDIT: 'edit',
DEFAULT: 'default'
};
Dieses Enum definiert zwei Modi:
- BEARBEITEN: Ermöglicht das Bearbeiten einer ausgewählten Zelle.
- STANDARD: Ermöglicht Navigation und Interaktion ohne Bearbeitung.
Warum Modi verwenden?
Modi vereinfachen das Management des UI-Zustands. Zum Beispiel bewegen sich in STANDARD-Modus Tastatureingaben zwischen Zellen, während sie sich im BEARBEITEN-Modus auf Zellinhalte auswirken.
2. Pub/Sub-Klasse
Das Pub/Sub-Modell verwaltet Abonnements und Live-Updates. Zellen können sich bei anderen Zellen abonnieren und sich dynamisch aktualisieren, wenn Abhängigkeiten sich ändern.
class PubSub {
constructor() {
this.map = {};
}
get(source) {
let result = [];
let queue = [ (this.map[source] || [])];
while (queue.length) {
let next = queue.shift();
result.push(next.toUpperCase());
if (this.map[next]) queue.unshift(this.map[next]);
}
return result;
}
subscribeAll(sources, destination) {
sources.forEach((source) => {
this.map[source] = this.map[source] || [];
this.map[source].push(destination);
});
}
}
Hauptfunktionen:
- Dynamisches Abhängigkeitsmanagement: Verfolgt Abhängigkeiten zwischen Zellen.
- Weitergabe von Aktualisierungen: Aktualisiert abhängige Zellen, wenn Ausgangszellen sich ändern.
- Breitensuche: Vermeidet Endlosschleifen, indem alle abhängigen Knoten verfolgt werden.
Beispiel Verwendung:
let ps = new PubSub();
ps.subscribeAll(['A1'], 'B1');
ps.subscribeAll(['B1'], 'C1');
console.log(ps.get('A1')); // Output: ['B1', 'C1']
3. Erstellen von Zeilen und Zellen
class Cell {
constructor(cell, row, col) {
cell.id = `${String.fromCharCode(col + 65)}${row}`;
cell.setAttribute('data-eq', '');
cell.setAttribute('data-value', '');
if (row > 0 && col > -1) cell.classList.add('editable');
cell.textContent = col === -1 ? row : '';
}
}
class Row {
constructor(row, r) {
for (let c = -1; c < 13; c++) {
new Cell(row.insertCell(), r, c);
}
}
}
Wichtige Funktionen:
- Dynamische Tabellenerzeugung: Ermöglicht das Hinzufügen von Zeilen und Spalten programmgesteuert.
- Zellenidentifikation: Generiert IDs basierend auf der Position (z. B. A1, B2).
- Bearbeitbare Zellen: Zellen sind nur bearbeitbar, wenn sie gültig sind (nicht Kopfzeilen/Spalten).
Warum dynamische Zeilen und Zellen verwenden?
Dieser Ansatz ermöglicht es, die Tabellengröße skalierbar und flexibel zu gestalten, um Funktionen wie das Hinzufügen von Zeilen oder Spalten ohne Änderung der Struktur zu unterstützen.
4. Ereignisbehandlung für Interaktion
addEventListeners() {
this.table.addEventListener('click', this.onCellClick.bind(this));
this.table.addEventListener('dblclick', this.onCellDoubleClick.bind(this));
window.addEventListener('keydown', this.onKeyDown.bind(this));
}
Wichtige Funktionen:
- Klickereignis: Markiert oder bearbeitet Zellen.
- Doppelklickereignis: Ermöglicht das Bearbeiten von Formeln.
- Tastendruckereignis: Unterstützt die Navigation mit den Pfeiltasten.
5. Formelparsing und Auswertung
function calcCell(expression) {
if (!expression) return 0;
return expression.split('+').reduce((sum, term) => {
let value = isNaN(term) ? getCellValue(term) : Number(term);
if (value === null) throw new Error(`Invalid cell: ${term}`);
return sum + Number(value);
}, 0);
}
Wichtige Funktionen:
- Dynamische Berechnung: Berechnet Formeln, die auf andere Zellen verweisen.
- Rekursive Auswertung: Löst verschachtelte Abhängigkeiten auf.
- Fehlerbehandlung: Identifiziert ungültige Verweise und zirkuläre Abhängigkeiten.
6. Fehlerbehandlung für Benutzereingaben
function isValidCell(str) {
let regex = /^[A-Z]{1}[0-9]+$/;
return regex.test(str);
}
Wichtige Funktionen:
- Validierung: Stellt sicher, dass Eingaben gültige Zell-IDs referenzieren.
- Skalierbarkeit: Unterstützt die dynamische Tabellenerweiterung, ohne die Validierung zu beeinträchtigen.
JavaScript-Themen behandelt
1. Ereignisbehandlung
Verwaltet Interaktionen wie Klicks und Tastendrücke.
window.addEventListener('keydown', this.onKeyDown.bind(this));
2. DOM-Manipulation
Erstellt und modifiziert DOM-Elemente dynamisch.
let cell = document.createElement('td'); cell.appendChild(document.createTextNode('A1'));
3. Rekursion
Verarbeitet Abhängigkeiten dynamisch.
function calcCell(str) { if (isNaN(str)) { return calcCell(getCellValue(str)); } }
4. Fehlerbehandlung
Erkennt ungültige Zellen und zirkuläre Abhängigkeiten.
if (!isValidCell(p)) throw new Error(`invalid cell ${p}`);
Conclusion
Dieses Projekt demonstriert eine leistungsstarke Tabellenkalkulation mit JavaScript. Es nutzt Ereignisbehandlung, Rekursion und Pub/Sub-Muster und legt den Grundstein für komplexe Webanwendungen. Erweitern Sie es, indem Sie Funktionen wie den Export von Daten, Diagramme oder Formatierungsregeln hinzufügen.
References
Source:
https://dzone.com/articles/spreadsheet-application-javascript-guide