דרך חדשה ליצור חוויות עריכה באינטרנט בהתאמה אישית באמצעות EditContext API

לא תמיד היה קל למפתחים לשלב יכולות עריכה מתקדמות באפליקציות האינטרנט שלהם. פלטפורמת האינטרנט מאפשרת לערוך גם טקסט פשוט וגם מסמכי HTML באמצעות רכיבים כמו <input> ו-<textarea>, או על ידי החלת המאפיין contenteditable על רכיבים. עם זאת, בדרך כלל היכולות הבסיסיות של סוגי הרכיבים האלה לא מספיקות למה שהמפתחים רוצים להשיג באפליקציות שלהם.

לרוב, המפתחים נאלצו להטמיע תצוגה מותאמת אישית משלהם של עורך, שמטמיע את הפונקציונליות שהמשתמשים שלהם זקוקים לה. תצוגת העריכה עשויה להיות בנויה באמצעות DOM מורכב – או אפילו באמצעות אלמנט <canvas> – אבל מכיוון שהדרך היחידה שבה המפתח יכול לקבל קלט טקסט דורשת אלמנט שאפשר להתמקד בו ולערוך אותו, הוא עדיין יצטרך להציב אלמנט contenteditable מוסתר במקום כלשהו בדף.

התוצאה היא שלמרות שהמשתמש נראה כאילו הוא עורך את התוכן ישירות בתצוגת העריכה בהתאמה אישית של האפליקציה, בפועל המפתח מקבל את הקלט עם פונקציות טיפול באירועים ברכיב המוסתר, ולאחר מכן משקף אותו בתצוגת העריכה הגלויה. המצב הזה עלול להוביל לבעיות, כי המפתח ייאלץ להתמודד עם התנהגות העריכה שמוגדרת כברירת מחדל בדפדפן ברכיב contenteditable המוסתר.

כדי לטפל בבעיות מהסוג הזה, צוות Microsoft Edge יזם את התקינה של EditContext, ממשק API חדש לפלטפורמת אינטרנט שמאפשר למפתחים לקבל קלט טקסט ישירות בלי להיות קשורים לפעולות העריכה שמוגדרות כברירת מחדל בדפדפן.

דוגמה מהעולם האמיתי

לדוגמה, כשמשתמשים עובדים יחד ב-Word Online. המשתמשים יכולים לערוך יחד ולראות את השינויים ואת מיקומי הסמן של כל אחד מהם. עם זאת, אם אחד השותפים משתמש בחלון של כלי עריכת שיטת קלט (IME) כדי לכתוב טקסט ביפנית, למשל, כלי העריכה שלו לא יתעדכן כדי להציג שינויים ממשתמשים אחרים עד שמשתמש ה-IME יסיים את הכתיבה. הסיבה לכך היא ששינויים באזור של DOM שעורכים בזמן שיש קומפוזיציה פעילה של IME עלולים לגרום לביטול מוקדם של הקומפוזיציה. האפליקציה צריכה להמתין עד שחלון ה-IME נסגר כדי לעדכן את התצוגה, מה שעלול לגרום לעיכובים ולפגוע בשיתוף הפעולה.

בעיות בשיתוף פעולה ב-Word Online בזמן כתיבת טקסט

כדי לספק חוויית משתמש ופיתוח טובה יותר, למפתחים צריכה להיות דרך לנתק את קלט הטקסט מתצוגת ה-DOM של HTML. הפתרון לבעיה הזו הוא EditContext API.

העקרונות הבסיסיים של EditContext

בעזרת EditContext, אפשר לקבל קלט של טקסט ותצוגה מפורטת ישירות דרך ממשק ה-API של EditContext, במקום באמצעות מעקב אחר שינויים ב-DOM. כך אפשר לשלוט בצורה הדוקה יותר באופן שבו המערכת מטפלת בקלט, ואפילו להוסיף יכולת עריכה לאלמנט <canvas>.

כשמשייכים מופע של EditContext לרכיב, אפשר לערוך אותו:

// This will be our editable element.
const element = document.querySelector('#editor-element');

// Creating the EditContext object.
const editContext = new EditContext();

// Associating the EditContext object with our DOM element.
// The element is now focusable and can receive text input.
element.editContext = editContext;

// In order to render the text typed by the user onto the
// page, as well as the user's selection, you'll need to
// receive the input in a textupdate event callback.
editContext.addEventListener('textupdate', event => {
  element.textContent = editContext.text;

  // For brevity, the code to render the selection
  // isn't shown here.
    renderSelection(event.selectionStart, event.selectionEnd);
 });

האחריות של המחבר

שימוש ב-EditContext API מאפשר לתמוך בקלות רבה יותר בשיטות קלט מתקדמות, כמו חלונות כתיבת IME, בוררי אמוג'י ומשטחי קלט אחרים של מערכת ההפעלה. כדי שכל זה יהיה אפשרי ברכיב שאפשר לערוך, נדרש מידע מסוים ב-EditContext API. בנוסף לעיבוד הטקסט והבחירה, יש עוד כמה דברים שצריך לעשות כשמשתמשים ב-EditContext API.

ניהול הצד של אזור שניתן לעריכה, או אם הבחירה של המשתמש משתנה

קוראים לשיטות updateControlBounds() ו-updateSelectionBounds() כדי להודיע למכונה של EditContext בכל פעם שגודל האזור שאפשר לערוך או הבחירה של המשתמש משתנים. כך הפלטפורמה יכולה להחליט איפה להציג חלונות של IME וממשקי עריכה אחרים ספציפיים לפלטפורמה.

// It's necessary to provide bounds information because EditContext
// is generic enough to work with any type of web editor, even
// <canvas>-based editors. The API doesn't make any assumptions as
// to how the editor is implemented or how the selection is rendered.
// Bounds are given in the client coordinate space.
const controlBound = editorElement.getBoundingClientRect();
const selection = document.getSelection();
const selectionBound = selection.getRangeAt(0).getBoundingClientRect();
editContext.updateControlBounds(controlBound);
editContext.updateSelectionBounds(selectionBound);

ניהול המיקום של ממשק המשתמש של העורך

אפשר להאזין לאירוע characterboundsupdate ולקרוא ל-updateCharacterBounds() בתגובה, כדי לעזור לפלטפורמה להחליט איפה להציג חלונות של IME וממשקי עריכה אחרים ספציפיים לפלטפורמה.

החלת עיצוב

מקשיבים לאירוע textformatupdate ומחילים את הפורמט שצוין באירוע על תצוגת העריכה. קישוטי הטקסט האלה משמשים את ממשקי ה-IME כשכותבים בשפות מסוימות. לדוגמה, ב-IME ביפנית יופיע קו תחתון כדי לציין איזה חלק מהטקסט נכתב כרגע.

צילום מסך של חלון של עורך שיטות קלט שמשמש להזנת תווים יפניים.

טיפול בהתנהגויות של עריכת טקסט עשיר

אפשר להאזין לאירוע beforeinput כדי לטפל בכל התנהגות עריכה של טקסט עשיר שרוצים לתמוך בה, כמו מקשי קיצור להדגשת טקסט או להוספת גופן נטוי, או החלת תיקון של בדיקת איות.

ניהול השינויים בבחירות של המשתמשים

כשהבחירה של המשתמש משתנה בגלל קלט מהמקלדת או מהעכבר, צריך להודיע על השינוי למכונה של EditContext. הדבר נדרש כי ממשק ה-API של EditContext רלוונטי למגוון רחב של תרחישים לדוגמה, כולל עורכים שנעשה בהם שימוש ברכיב <canvas>, שבהם הדפדפן לא יכול לזהות שינויים בבחירה באופן אוטומטי.

document.addEventListener('selectionchange', () => {
  const selection = document.getSelection();

  // EditContext doesn't handle caret navigation, so all the caret navigation/selection that happens
  // in DOM space needs to be mapped to plain text space by the author and passed to EditContext.
  // This example code assumes the editable area only contains text under a single node.
  editContext.updateSelection(selection.anchorOffset, selection.focusOffset);
});

אם האלמנט שבו אתם משתמשים עם EditContext הוא אלמנט <canvas>, תצטרכו גם להטמיע התנהגויות של בחירה וניווט באמצעות סמן הטקסט, כמו ניווט בטקסט באמצעות מקשי החיצים. בנוסף, בדיקת האיות המובנית בדפדפן פועלת רק ברכיבים שאינם <canvas>.

EditContext לעומת contenteditable

כדאי להשתמש ב-EditContext אם אתם מטמיעים עורך עם תכונות מלאות ואתם רוצים לשלוט באופן מלא באופן שבו מתבצע הטיפול בקלט הטקסט, או אם אתם מוסיפים תכונות מתקדמות כמו עריכה משותפת עם כמה משתמשים. עם זאת, לאור כל הדרישות הקודמות לשימוש ב-EditContext, אם כל מה שאתם צריכים הוא תמיכה פשוטה בעריכת טקסט, סביר להניח שעדיין כדאי להשתמש ברכיבים <input> ו-<textarea> או במאפיין contenteditable.

ומה בעתיד?

צוות Microsoft Edge הטמיע את EditContext ב-Chromium בשיתוף פעולה עם מהנדסי Chrome, והוא מופיע בגרסה 121 (ינואר 2024) של Chrome ושל Edge. בשלב הזה, הוא זמין רק בדפדפנים המבוססים על Chromium, אבל אפשר לקרוא את העמדות של Mozilla ושל WebKit בנושא EditContext API.

אנחנו רוצים להקל על מפתחי אתרים ליצור חוויות עריכה חזקות בהתאמה אישית באינטרנט, ואנחנו מאמינים ש-EditContext API עוזר להשיג את המטרה הזו על ידי פתרון אתגרים קיימים ומתן דרך ישירה יותר לטיפול בהזנת טקסט.

מידע נוסף על ה-API זמין במסמכי העזרה של MDN. כדי לשלוח משוב על העיצוב של ה-API, פותחים פנייה במאגר GitHub של EditContext API. כדי לדווח על באגים ביישום ה-API, שולחים באג אל crbug.com.