דרך חדשה ליצור חוויות עריכה באינטרנט בהתאמה אישית באמצעות 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 אפשר לקבל קלט של טקסט ויצירה ישירות דרך הפלטפורמה EditContext API, במקום לצפות בשינויים ב-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.