תמיכה בשכבה העליונה בכלי הפיתוח ל-Chrome

Alina Varkki
Alina Varkki

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

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

מהם רכיבי השכבה העליונה והשכבה העליונה?

מה בדיוק קורה באופן פנימי כשפותחים <dialog> כמודל? 🤔

הוא מוצב בשכבה עליונה. התוכן בשכבה העליונה עובר עיבוד מעל כל שאר התוכן. לדוגמה, תיבת דו-שיח של מודל צריכה להופיע מעל כל תוכן DOM אחר, כך שהדפדפן מעבד באופן אוטומטי את הרכיב הזה ב'שכבה עליונה' במקום לאלץ את המחברים להילחם ב-z-index באופן ידני. רכיב שכבה עליונה מופיע מעל רכיב גם עם z-index הגבוה ביותר.

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

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

בודקים את ההטמעה של תיבת הדו-שיח הבאה:

<main>
  <button onclick="window.dialog.showModal();">Open Dialog</button>
</main>
<dialog id="dialog"></dialog>

הנה הדגמה עם כמה תיבות דו-שיח שבהן מוחלים סגנונות על הרקע שלהן (תמונות רקע שמתוארות בהמשך):

מה זה 'רקע'?

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

לכל רכיב בשכבה העליונה יש פסאודו-רכיב של CSS שנקרא רקע.

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

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

כך מעצבים את הרקע:

/* The browser displays the backdrop only when the dialog.showModal() function opens the dialog.*/
dialog::backdrop {
    background: rgba(255,0,0,.25);
}

איך להציג רק את הרקע הראשון?

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

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

אם הרכיב שנוסף הוא לא הראשון בשכבה העליונה, הפונקציה שנקראה כשמציבים את הרכיב בשכבה העליונה, מחילה מחלקה hiddenBackdrop על ::backdrop. המחלקה הזו מוסרת לאחר הסרת הרכיב מהשכבה העליונה.

כדאי לבדוק את הקוד בהדגמה הזו:

עיצוב תמיכה בשכבה עליונה בכלי הפיתוח

התמיכה של כלי הפיתוח בשכבה העליונה עוזרת למפתחים להבין את הרעיון של השכבה העליונה ולראות איך התוכן של השכבה העליונה משתנה. התכונות האלה עוזרות למפתחים לזהות את הפרטים הבאים:

  • הרכיבים בשכבה העליונה בכל רגע נתון והסדר שלהם.
  • הרכיב שבחלק העליון של המקבץ בכל שלב.

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

תכונות התמיכה בשכבה העליונה מאפשרות לכם:

  1. לבדוק אילו רכיבים נמצאים בסטאק השכבה העליונה בכל רגע נתון. מקבץ הייצוגים של השכבה העליונה משתנה באופן דינמי ככל שמוסיפים או מסירים רכיבים מהשכבה העליונה.
  2. אפשר לראות את מיקום הרכיב במקבץ השכבה העליונה.
  3. מעבר מהרכיב או מהרכיבים של השכבה העליונה רכיב פסאודו-אלמנט של רקע בעץ לאלמנט או רכיב פסאודו של רקע במכל לייצוג השכבה העליונה ובחזרה.

בואו נראה איך משתמשים בתכונות האלה!

מאגר שכבה עליונה

כדי להמחיש את רכיבי השכבה העליונה, כלי הפיתוח מוסיפים מאגר שכבה עליונה לעץ הרכיבים. הוא נמצא אחרי התג הסוגר </html>.

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

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

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

דילוג מקישור הקונטיינר של השכבה העליונה לרכיב.

כדי לדלג מהרכיב של עץ השכבה העליונה לקישור שבמאגר השכבה העליונה, לוחצים על התג שכבה עליונה שלצד הרכיב.

דילוג מרכיב לקישור למאגר התגים של השכבה העליונה

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

השבתת התג מתבצעת.

סדר הרכיבים במקבץ השכבה העליונה

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

התגים שליד רכיבי העץ מציינים אם הרכיבים שייכים לשכבה העליונה ומכילים את מספר המיקום של הרכיב במקבץ.

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

סדר הרכיבים במקבץ.

רקעים בקונטיינר של השכבה העליונה

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

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

מיקום ערימת הקולאז&#39;ים.

שינויים בעץ ה-DOM

ElementsTreeElement, המחלקה שאחראית ליצירה ולניהול של רכיבי עץ DOM בודדים בכלי הפיתוח, לא הספיקה להטמעת קונטיינר של שכבה עליונה.

כדי להציג את מאגר השכבה העליונה כצומת בעץ, הוספנו מחלקה חדשה שיוצרת צמתים של רכיבים בעץ של כלי פיתוח. בעבר, הכיתה שאחראית ליצירת עץ של רכיבי פיתוח הופעלה בכל TreeElement ב-DOMNode, שהיא מחלקה עם backendNodeId ומאפיינים אחרים שקשורים לקצה העורפי. בשם backendNodeId, האפליקציה מוקצית בקצה העורפי.

צומת הקונטיינר של השכבה העליונה, שכולל רשימת קישורים לרכיבים בשכבה העליונה, נדרש כדי להתנהג כצומת של רכיב עץ רגיל. עם זאת, הצומת הזה אינו 'אמיתי' צומת ה-DOM והקצה העורפי לא צריכים ליצור את צומת הקונטיינר של השכבה העליונה.

כדי ליצור צומת קצה שמייצג את השכבה העליונה, הוספנו סוג חדש של צומת קצה קדמי שנוצר ללא DOMNode. רכיב הקונטיינר של השכבה העליונה הוא הצומת הראשון של הקצה הקדמי שלא מכיל DOMNode, כלומר הוא קיים רק בקצה העורפי והקצה העורפי לא יודע על הנושא. כדי שההתנהגות תהיה זהה לזו של צמתים אחרים, יצרנו מחלקה חדשה של TopLayerContainer שמרחיבה את המחלקה UI.TreeOutline.TreeElement, שאחראית להתנהגות של הצמתים בממשק הקצה.

כדי להשיג את המיקום הרצוי, המחלקה שמעבדת את הרכיב מצרפת את TopLayerContainer כאחות הבאה של התג <html>.

תג חדש של שכבה עליונה מציין שהרכיב נמצא בשכבה העליונה ומשמש כקישור לקיצור הדרך של הרכיב הזה ברכיב TopLayerContainer.

עיצוב ראשוני

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

הסיכון שהגענו אליו היה יצירת קישורים לצומתי DOM בחזית במקום לשכפל את הצמתים האלה. הכיתה שאחראית ליצירת קישורים לרכיבים בכלי פיתוח היא ShortcutTreeElement, והיא מרחיבה את הקטע UI.TreeOutline.TreeElement. ההתנהגות של ShortcutTreeElement זהה לזו של רכיבי עץ DOM אחרים של כלי פיתוח, אבל אין לו צומת תואם בקצה העורפי ולחצן שמקשר אל ElementsTreeElement. לכל ShortcutTreeElement לצומת של השכבה העליונה יש צאצא ShortcutTreeElement שמקשר לייצוג של פסאודו-רכיב ::backdrop בעץ ה-DOM של כלי הפיתוח.

עיצוב ראשוני:

עיצוב ראשוני.

שינויים בכלי הפיתוח ל-Chrome (CDP)

כדי להטמיע את התמיכה בשכבה העליונה, צריך לבצע שינויים ב-Chrome DevTools Protocol (CDP). CDP משמש כפרוטוקול תקשורת בין כלי הפיתוח ל-Chromium.

עלינו להוסיף את הפרטים הבאים:

  • פקודה להפעלה מהחזית בכל שלב.
  • אירוע שיופעל בקצה העורפי.

CDP: הפקודה DOM.getTopLayerElements

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

  # Returns NodeIds of the current top layer elements.
  # Top layer renders closest to the user within a viewport, therefore, its elements always
  # appear on top of all other content.
  experimental command getTopLayerElements
    returns
      # NodeIds of the top layer elements.
      array of NodeId nodeIds

CDP: אירוע אחד (DOM.topLayerElementsUpdated)

כדי לקבל את הרשימה העדכנית של רכיבי השכבה העליונה, אנחנו צריכים כל שינוי ברכיבי השכבה העליונה כדי להפעיל אירוע CDP ניסיוני. האירוע הזה מיידעים את הקצה הקדמי של השינוי שקורא לפקודה DOM.getTopLayerElements ומקבל את רשימת הרכיבים החדשה.

האירוע נראה כך:

  # Called by the change of the top layer elements.
  experimental event topLayerElementsUpdated

שיקולים לגבי CDP

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

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

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

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