גיליונות נתונים הפכו לחלק בלתי נפרד מהחישוב היומיומי המודרני. הם מאפשרים למשתמשים לארגן, לערוך ולנתח נתונים בפורמט טבלאי. אפליקציות כמו Google Sheets קובעות את התקן לגבי גיליונות נתונים עוצמתיים ואינטראקטיביים.
בפוסט בלוג זה, נלמד איתך את התהליך של בניית אפליקציית גיליון נתונים באמצעות JavaScript. נתמקד במושגים פרומטים, נחקור בתכונות של JavaScript, ונכלול קטעי קוד מפורטים עם הסברים.
כל קוד המקור זמין כאן ב-Codepen שלי.
מהו גיליון הנתונים של Google?
גיליון הנתונים של Google הוא אפליקציה מבוססת רשת שמאפשרת למשתמשים ליצור, לערוך ולשתף פעולה על גיליונות נתונים באינטרנט. הוא מספק תכונות כמו נוסחאות, אימות נתונים, תרשימים ועיצוב תנאי.
הפרוייקט שלנו מדמה תכונות מרכזיות של גיליון הנתונים של Google, ומתמקד ב:
- תאים שניתנים לעריכה.
- פענוח והערכה של נוסחאות.
- עדכונים חיים דרך מודל Pub/Sub.
- ניווט במקלדת ובחירת תאים.
- הערכת תלות דינמית בין תאים.
תכונות של הפרוייקט הזה
- תאים שניתנים לעריכה: מאפשר למשתמשים להזין טקסט או משוואות לתאים.
- תמיכה בנוסחאות: מעבד נוסחאות שמתחילות ב-
=
ומעריכות ביטויים. - עדכונים חיים: שינויים בתאים תלויים גורמים לעדכונים באמצעות דגם פרסום/הרשמה.
- ניווט במקלדת: מאפשר תנועה בין התאים באמצעות חצים.
- הערכה דינמית: מבטיח עדכונים בזמן אמת עבור נוסחאות התלויות בתאים אחרים.
- טיפול בשגיאות: מספק הודעות שגיאה משמעותיות עבור קלטים לא תקינים או תלות מעגלית.
- עיצוב נפח: מאפשר הרחבה קלה כדי להוסיף יותר שורות, עמודות או תכונות.
רכיבי מפתח של היישום
1. ניהול מצב
const Mode = {
EDIT: 'edit',
DEFAULT: 'default'
};
סוג זה מגדיר שני מצבים:
- עריכה: מאפשר עריכה של תא נבחר.
- ברירת מחדל: מאפשר ניווט ואינטראקציה ללא עריכה.
למה להשתמש במצבים?
מצבים מפשטים את ניהול מצב הממשק המשתמש. לדוגמה, במצב ברירת מחדל, קלטי מקלדת מעבירים בין תאים, בעוד במצב עריכה, קלטים משנים את תוכן התא.
2. מחלקת פרסום/הרשמה
דגם הפרסום/הרשמה עוסק בהרשמות ובעדכונים חיים. תאים יכולים להירשם לתאים אחרים ולעדכן באופן דינמי כאשר תלות משתנה.
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);
});
}
}
תכונות מרכזיות:
- ניהול תלות דינמי: מעקב אחר תלות בין תאים.
- העברת עדכונים: מעדכן תאים תלויים כאשר תאים מקור משתנים.
- חיפוש ברחבת ראשונה: ממנע לולאות אינסופיות על ידי מעקב אחר כל הצמתים התלויים.
שימוש דוגמתי:
let ps = new PubSub();
ps.subscribeAll(['A1'], 'B1');
ps.subscribeAll(['B1'], 'C1');
console.log(ps.get('A1')); // Output: ['B1', 'C1']
יצירת שורות ותאים
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);
}
}
}
מאפיינים עיקריים:
- יצירת טבלה דינמית: מאפשר הוספת שורות ועמודות באופן תכנותי.
- זיהוי תאים: יוצר זיהויים על פי מיקום (לדוגמה, A1, B2).
- תאים ניתנים לעריכה: תאים ניתנים לעריכה רק אם הם תקפים (לא שורות/עמודות כותרת).
למה להשתמש בשורות ותאים דינמיים?
הגישה זו מאפשרת לגודל הטבלה להיות גמיש ונתמך, תומכת בתכונות כמו הוספת שורות או עמודות בלי לשנות את המבנה.
טיפול באירועים לצורך אינטראקציה
addEventListeners() {
this.table.addEventListener('click', this.onCellClick.bind(this));
this.table.addEventListener('dblclick', this.onCellDoubleClick.bind(this));
window.addEventListener('keydown', this.onKeyDown.bind(this));
}
מאפיינים עיקריים:
- אירוע לחיצה: בוחר או עורך תאים.
- אירוע לחיצה כפולה: מאפשר עריכת נוסחאות.
- אירוע Keydown: תומך בניווט עם מקשי חץ.
ניתוח והערכה של נוסחאות
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);
}
מאפיינים עיקריים:
- חישוב דינמי: מחשב נוסחאות המתייחסות לתאים אחרים.
- הערכה רקורסיבית: פותר תלותות מקוננות.
- טיפול בשגיאות: מזהה התייחסויות לא תקינות ותלותות מעגליות.
טיפול בשגיאות בקלט מהמשתמש
function isValidCell(str) {
let regex = /^[A-Z]{1}[0-9]+$/;
return regex.test(str);
}
מאפיינים עיקריים:
- אימות: מבטיח שהקלט מתייחס לזיהויים תקפים של תאים.
- גמישות: תומך בהרחבת טבלה דינמית מבלי לשבור את האימות.
נושאים ב-JavaScript שנעסקו בהם
1. טיפול באירועים
מנהל אינטראקציות כמו לחיצות ומקשי קלדה.
window.addEventListener('keydown', this.onKeyDown.bind(this));
2. מניפולציית DOM
יוצר ומשנה אלמנטים של DOM באופן דינמי.
let cell = document.createElement('td'); cell.appendChild(document.createTextNode('A1'));
3. חזרה ריקורסיבית
מעבד תלותים באופן דינמי.
function calcCell(str) { if (isNaN(str)) { return calcCell(getCellValue(str)); } }
4. טיפול בשגיאות
מגלה תאים לא חוקיים ותלותות מעגליות.
if (!isValidCell(p)) throw new Error(`invalid cell ${p}`);
סיכום
הפרויקט הזה מדגים גיליון אלקטרוני חזק באמצעות JavaScript. הוא משתמש בטיפול באירועים, חזרה ריקורסיבית ודפוסי Pub/Sub, ומניח את היסודות ליישומי אינטרנט מורכבים. הרחב אותו על ידי הוספת תכונות כמו ייצוא נתונים, גרפים או כללי עיצוב.
מקורות
Source:
https://dzone.com/articles/spreadsheet-application-javascript-guide