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

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

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

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

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

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

תמיכה בעיצוב של השכבה העליונה ב-DevTools

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

השבתת התג.

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

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

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

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

הסדר של הרכיבים בסטאק.

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

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

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

מיקום הסטאק של הרקעים.

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

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

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

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

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

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

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

עיצוב ראשוני

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

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

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

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

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

כדי להטמיע את התמיכה בשכבה העליונה, נדרשים שינויים בפרוטוקול של כלי הפיתוח ל-Chrome‏ (CDP). CDP משמש כפרוטוקול תקשורת בין DevTools לבין 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 כדי לקבל רשימה של רכיבים מעודכנים. אם נשלח רשימה של רכיבים או רכיב ספציפי שגרמו לשינוי בכל פעם שמופעל אירוע, נוכל לחסוך שלב אחד של קריאה לפקודה. עם זאת, במקרה כזה, לקצה הקדמי לא תהיה שליטה על האלמנטים שיועברו.

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