המדריך ה(לא)שלם ל: z-index

משך זמן קריאה: 8 דקות

למרות אין סוף פוסטים שנכתבו בנושא, ולא משנה כמה קראתי וכמה הבנתי (או אולי לא הבנתי לגמרי…), תמיד תהיה הפעם הזאת שלא אבין למה לעזאזל אלמנט אחד עולה מעל אלמנט אחר. אני בטוחה שאני לא היחידה שעדיין נתקלת בסיטואציה הזאת, וכמו לדבג css בעזרת הצבע red, אין אחד או אחת שלא שינתה את הערך של z-index ל-9999 רק כדי לקוות בסתר הלב שעוד 9 יפתור את הבעיה 😉

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

מסכמת את ההבנות שלי כשחקרתי מה קורה ואיך הכל עובד, מקווה קצת לפשט את הנושא, ולהביא כמה מקורות קריאה מעולים (בסוף הפוסט) שיסבירו, יבהירו ואולי אחת ולתמיד יעשו סדר בכל הנושא של z-index.

שכבות (וציר ה-z)

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

בדפדפן ההתנהגות די דומה, אבל יחד עם זאת ממש לא. בדפדפן, אלמנטים יכולים לזוז על ציר ה-x, על ציר ה-y ועל ציר ה-z, שהוא בעצם מעין ציר עומק (כמו ציר השכבות בפוטושופ).
אבל אם בפוטושופ גלוי וידוע לנו איזה שכבה באה מעל מי ואיך היא משפיעה על שכבות שהיא מעליהן, אזי ב-html אנחנו נראה את המבנה של ה״קבוצות״ של שכבות שלנו במבנה ה-html, אבל עד שלא נצלול פנימה, ונבין איך ה-css משפיע, לא נדע בוודאות מה הסדר של השכבות על פני ציר ה-z.

סדר ראשוני

סדר זה נקרא root stacking context או local stacking context. בברירת המחדל, אם נתייחס לדוגמה ל-div-ים הראשיים של הדפדפן, כל אחד מהם מופיע אחד אחרי השני, אבל ה-div השני גם יופיע גבוה יותר בהיררכה של ציר ה-z , השלישי יותר גבוה מהשני וכן הלאה.

בדוגמת הקוד הבאה אפשר לראות את האלמנטים בסדר ההופעה ה-html-לי שלהם – כל האלמנטים הם display: inline-block ויש להם הגדרה של margin שלילי ואפשר לראות את השכבות שהם יוצרים:

See the Pen Understanding z-index – 1 by Rachel Bratt Tannenbaum (@rachelbt) on CodePen.

שינוי סדר השכבות – stacking context

בתוך אותו הסדר, נוכל לשנות את סדר גובה השכבות, אפילו עוד לפני שנדבר על המאפיין z-index. ברגע שנתנו לאחד ה-div-ים מאפיין שיש לו את היכולת לשנות את הסדר השכבות (stacking contex), הוא מקבל קדימות והוא עובר לשכבה גבוהה יותר. ומה הם המאפיינים האלה?

המאפיין position, על כל הערכים שלו (למעט static). בעיקרון, סה״כ הגיוני שאם נתנו לאלמנט הגדרה של position, אנחנו רוצים לשלוט על המיקום שלו ביחס לעצמו או ביחס לאחרים. זאת אומרת שאם למשל יהיו לנו 4 אלמנטים, ונרצה שהאלמנט הראשון יהיה מעל השני – כל שנזדקק לתת לו הוא את אחד המאפיינים שייצרו stacking contex חדש והופ, בלי שום שימוש ב z-index העלינו אותו למעלה.

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

  • transform
  • perspective
  • אלמנט עם ערך של z-index שונה מ auto שאבא שלו בעל המאפיין display:flex
  • אלמנט עם ערך של z-index שונה מ auto שאבא שלו בעל המאפיין display:grid

וחלקם פחות:

  • אלמנט שנתנו לו ערך של opacity נמוך מ-1
  • אלמנט שהוחל עליו filter
  • ועוד…

לרשימה המלאה של מאפיינים המשפיעים על layering context

בדוגמת הקוד הבאה, כל האלמנטים קיבלו את אותן ההגדרות כמו מקודם, למעט 2 שינויים – אלמנט מספר 1 קיבל הגדרה של transform: translateX ואלמנט 4 קיבל הגדרה של opacity: 0.99.

See the Pen Understanding z-index – 1 by Rachel Bratt Tannenbaum (@rachelbt) on CodePen.

המאפיינים האלה גרמו לאלמנטים לקבל stacking context חדש, והיות ולאלמנטים האחרים אין שום מאפיין מיוחד, אלמנטים מספר אחת וארבע עולים מעליהם.
תוצאה זהה הייתה מתקבלת אם היינו נותנים את אחד מהמאפיינים האחרים ברשימה (כמו למשל position:relative).
אבל אם לכל האלמנטים הייתה הגדרה של שינוי stacking context, למשל אם לכולם הייתה הגדרה של position:relative, אזי תוספת של opacity או transform לא הייתה משפיעה על אלמנטים אחת וארבע היות וגם כל שאר האלמנטים כבר קיבלו מאפיין ששינה את ה-stacking context שלהם ושוב, כולם ״שווים״. במצב הזה, כדי להעלות את אחד מהאלמנטים שלנו על פני ציר ה-z, נזדקק למאפיין נוסף והוא הz-index.

הוספת המאפיין z-index

לפני שנתחיל, חשוב לדעת שהמאפיין z-index לא ישפיע על אלמנט אלא אם הוגדר לו גם position (שוב, למעט static).

נחזור לדוגמה הקודמת, ונוסיף לכל האלמנטים position: relative. התוספת הזו תחזיר אותנו לנקודת המוצא שאיתה פתחנו – שתיים עולה על אחת, שלוש על שתיים וכך הלאה

אותה דוגמת קוד כמו הקודמת, רק שהוספתי לאלמנטים את המאפיין position: relative

See the Pen Understanding z-index – 2 by Rachel Bratt Tannenbaum (@rachelbt) on CodePen.

הפעם, האלמנטים שהוספנו להם מאפיינים (אלמנט 1 ואלמנט 4) לא ישנו את מיקומם על פני ציר ה-z. זה מפני שלא משנה אם יש להם מספר רב יותר של מאפיינים שמשנים את ה-stacking context – אין פה כפל מבצעים וממש מספיק מאפיין אחד להוציא מהקונטקסט. רק האלמנט שיקבל את המאפיין z-index (בנוסף למאפיין position) יקבל קדימות על ציר ה-z, והאלמנט שיקבל את הערך המספרי הגבוה ביותר של z-index יהיה העליון מבניהם.

בסוגריים אוסיף שאם כולם יקבלו את אותו ערך של z-index עדיין נישאר עם אותה התוצאה.

בדוגמה הבאה כל האלמנטים מוגדרים עם position: relative, אבל רק לאלמנט 1, 3 ו-5 יש הגדרה של z-index:1. ש״מעלה״ אותם שכבה.

See the Pen Understanding z-index – 2 by Rachel Bratt Tannenbaum (@rachelbt) on CodePen.

stacking context וילדים

הוספתי לכל האלמנטים בדוגמה הקודמת אלמנט-בן שהוא tooltip, ורק לשם ההדגמה הוא מוצג תמיד. כדי למקם אותו נתתי לו position:absolute, וכמובן האלמנט העוטף קיבל position:relative. ה-tooltip שאבא שלו הוא האחרון שמופיע בסדר ה-html יהיה הגבוה ביותר גם על פני ציר ה-z.

בדוגמה הבאה כל האלמנטים מוגדרים עם position: relative, ולכולם אלמנט בן עם position:absolute

See the Pen Understanding z-index – with tooltip by Rachel Bratt Tannenbaum (@rachelbt) on CodePen.

אם אחד מה-tooltip-ים יקבל z-index עם ערך גבוה יותר מ-1, הוא יהיה גבוה משאר האלמנטים, אפילו אם בסדר ה-html-לי האבא שלו מופיע בהיררכיה נמוכה יותר. כלומר, אם למשל ניתן לאחד מאלמנטי האב שלנו את ההגדרה של z-index:2, אז הבן שלו יופיע גבוה יותר משאר הבנים האחרים.

מה יקרה אבל אם אחר כך ניתן לאחד הבנים האחרים ערך גבוה יותר של z-index? הוא יופיע גבוה יותר מאשר זה שהאבא שלו עם z-index:2. לכאורה, ההגיון היה אומר שאם לאבא יש z-index גבוה יותר, אזי גם הילדים שלו יהיו גבוהים יותר. אבל ברגע שהגדרנו גם לילדים position: absolute, יצרנו להם stacking context חדש ולכן הם נכנסים לאותו חישוב מספרי שבו ערך גבוה יותר של z-index מנצח.

אפשר גם אחרת: isolation

כדי להמנע ממצבים כאלה, של ״מלחמות״ z-index(בהנחה כמובן שאנחנו שולטים בכל ה-css), יש דרך לשלוט בסיטואציה הנ״ל בעזרת ההגדרה של isolation: isolate;.

מה אנחנו משיגים עם זה?

isolation בעצם עושה מעין scoping לאלמנטים ומשאיר את ה z-index בתוך האבא שלו בלי להשפיע על שאר האלמנטים בעמוד.
אנחנו כופים על האלמנט שלנו לייצר stacking context חדש (אפילו אם הוא מלכתחילה כבר יצר אחת. הרי כבר הגדרנו על האלמנט position:relative). מה ש״מוריד״ את האלמנט שוב בהיררכיית השכבות, ושוב, לא משנה אם ניתן לילד שלו z-index של 9999 הוא עדיין יהיה ״נעול״ לסביבה שלו.

נחזור שוב לדוגמה שלנו של ה-tooltip. לכל האלמנטים מוגדר position:relative, ולכל הילדים position:absolute;. האלמנט האחרון שנרשם ב-html הוא הגבוה ביותר על ציר ה-z. אבל אם ניתן ל-tooltip של האלמנט הראשון (האדום) z-index:1 הוא יופיע מעל כל שאר האלמנטים וככה זה יראה:

אם נוסיף לאלמנט הראשון (האדום) את ההגדרה isolation: isolate; נשנה את ה-stacking context, ונחזיר אותו ל״מקום״ הראשוני שלו בהיררכיה על פני ציר ה-z.

See the Pen Understanding z-index – isolated by Rachel Bratt Tannenbaum (@rachelbt) on CodePen.

יוצא דופן

שניה לפני שמסיימים, יוצאי דופן:

זוכרים שכתבתי שהמאפיין z-index לא ישפיע אם לא נגדיר גם position עליו? אז קבלו את יוצאי הדופן – ילדים של אלמנט עם dispaly:grid או dispaly:flex. מה שאומר שאם נוסיף לאחד הילדים של אבא עם display:flex את הערך של z-index:1; הוא יהיה מעל האחים שלו.

בדוגמה הבאה, כל המעטפת קיבלה הגדרה של display: flex; ולאף אחד מהילדים אין הגדרה של position, מה שאומר שלכאורה z-index לא אמור להשפיע. אבל הוא כן משפיע, כי האלמנטים הם ילדים של flex

See the Pen Understanding z-index – 1 by Rachel Bratt Tannenbaum (@rachelbt) on CodePen.

מקורות מידע

תוספות (07.2023)

בדפדפנים יש שכבה חדשה שנקראת top-layer שהיא העליונה מכולם, ובעצם פותרת את בעיית ה z-index לאלמנטים שצריכים להיות מוגבהים משאר התוכן כמו מודל או פופאפים. כשמשתמשים במתודה של showModal על אלמנט dialog הדיאלוג נפתח באופן אוטומטי ב top layer.

popover הוא attribute, חדש שמתחיל להכנס לדפדפנים שההוספה שלו תעביר את האלמנט ל top-layer. הפיצ׳ר הזה עדיין לא נתמך מספיק ויש לו עוד בעיות מיקום, אבל ללא ספק הכיוון מבטיח 🙂

Offset parent and stacking context: positioning elements in all three dimensions – פוסט של שעוזר מאוד להבין מה זה stacking context ואיך הוא מתנהג

  4 תגובות ל “המדריך ה(לא)שלם ל: z-index

כתיבת תגובה

האימייל לא יוצג באתר.