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

פורסם: 27 במרץ 2026

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

סרטון לקידום: דמיינו מחדש את האינטרנט עם מעברים מוגבלים לתצוגה. לנסות הדגמה חיה (Chrome 147 ואילך)

הצורך במעברים בין תצוגות עם היקף צר יותר

כשמתחילים מעבר תצוגה באותו מסמך באמצעות document.startViewTransition() (או באמצעות הגרסה המקבילה שלו במסמך אחר), הדפדפן מגדיר את מעבר התצוגה שמתקבל בהיקף המסמך.

אחרי שהקריאה החוזרת לעדכון מופעלת והדפדפן מצלם את כל הרכיבים הנדרשים, שכבת העל ::view-transition והעץ של רכיבי פסאודו שנוצרים ממנה מצורפים לרכיב :root, html בדוגמה הבאה.

html
  ├─ ::view-transition
  │  └─ ::view-transition-group(root)
  │     └─ ::view-transition-image-pair(root)
  │        ├─ ::view-transition-old(root)
  │        └─ ::view-transition-new(root)
  ├─ head
  └─ body
     └─ …

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

הדגמה חיה

הקלטת הדגמה

הפעלה מחדש של pointer-events ב-::view-transition או שימוש בקבוצות מעבר תצוגה מקוננות יכולים לפתור חלק מתופעות הלוואי שמעברי תצוגה בהיקף מסמך יוצרים. עם זאת, השיטות האלה לא יכולות לפתור את כל הבעיות.

לדוגמה, אלמנטים עם position: fixed או חלונות קופצים עדיין מוסתרים על ידי מעבר תצוגה בהיקף המסמך בזמן שהמעבר פעיל – מצב שנקרא גם בעיית z-index.

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

הדגמה חיה

הקלטת הדגמה

פתרון עקיף אחד הוא ללכוד את popover כחלק ממעבר התצוגה על ידי הקצאת view-transition-name. השיטה הזו יכולה לעבוד עבור מופע יחיד, אבל היא מסורבלת לתחזוקה ומעמיסה על תהליך יצירת התמונות.

מעברי תצוגה בהיקף של רכיב

מעברים בין תצוגות בהיקף של רכיב מאפשרים להתחיל מעבר בין תצוגות בעץ משנה של ה-DOM. במקום להתקשר אל document.startViewTransition(), מתקשרים אל element.startViewTransition() ברכיב שרירותי, וכך מגדירים את היקף המעבר בין התצוגות לרכיב הזה.

בקטע הקוד הבא, הדפדפן מתחיל מעבר תצוגה בהיקף רכיב ברכיב <ul>.

document.querySelector('ul').startViewTransition({
  callback: () => {
    // … code that manipulates the contents of <ul>
  },
})

האלמנט שבו מפעילים את element.startViewTransition() – לדוגמה, <ul> – נקרא שורש המעבר או ההיקף.

כשדפדפן מצמצם את היקף המעבר בין תצוגות לרכיב מסוים, הוא מבודד אותו משאר ה-DOM:

  • הדפדפן מחפש רכיבים לצילום תמונת מצב רק בעץ המשנה של ההיקף.
  • במהלך תהליך יצירת התמונה – בזמן ההפעלה של הקריאה החוזרת update – רק העיבוד של ההיקף נעצר.
  • עץ פסאודו ::view-transition שנוצר מוזרק לשורש המעבר.

לדוגמה, עם <ul>, עץ ה-DOM נראה כך בזמן שהנפשת המעבר פעילה:

html
  ├─ head
  └─ body
     ├─ ul
     │  ├─ ::view-transition
     │  │  └─ ::view-transition-group(root)
     │  │     ├─ ::view-transition-group-children(root)
     │  │     │  └─ …
     │  │     └─ ::view-transition-image-pair(root)
     │  │        ├─ ::view-transition-old(root)
     │  │        └─ ::view-transition-new(root)
     │  ├─ li
     │  ├─ li
     │  └─ li
     ├─ button#showpopover
     ├─ button#reorder
     └─ div#popover
        └─ p

האלמנט ::view-transition pseudo הוא באותו גודל ובאותה צורה כמו רכיב השורש של המעבר, והוא מוצג רק מעל רכיב השורש של המעבר. לכן, סדר השכבות של רכיבים מחוץ לשורש המעבר נשמר.

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

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

הדגמה חיה

הקלטת הדגמה

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

בנוסף, הרכיבים שמחוץ לרכיב <ul>, כמו הכפתורים, נשארים אינטראקטיביים כי הם לא חלק מההיקף.

היקפים עם השתתפות עצמית וקבוצות מקוננות של מעברי תצוגה

כשמתחילים מעבר תצוגה עם היקף של רכיב ברכיב שחלקים ממנו חורגים מהגבולות (כלומר, כשמאפיין overflow שלו מוגדר ל-hidden, ל-scroll או ל-clip), אפשר לראות שהתוכן של מעבר התצוגה נשאר חתוך.

הסיבה לכך היא שמעברים בין תצוגות בהיקף של רכיב מטפלים באופן אוטומטי בפעולות הבאות:

  • ההיקף מוחל באופן אוטומטי, ולכן הוא משתתף בעצמו.view-transition-name: root
  • ההיקף מוחל באופן אוטומטי על view-transition-group: contain כדי להפעיל קבוצות מקוננות של מעברי תצוגה.
  • אם שורש ההיקף חותך את הגלישה שלו, ::view-transition-group-children(root) פסאודו שנוצר חותך באופן אוטומטי את התוכן שלו באמצעות overflow: clip, וכך מונע את הגלישה של הפסאודו מחוץ לשורש המעבר.

כתוצאה מכך, תוכלו להשתמש ב-CSS רק ברכיבים שאתם רוצים לכלול במעברי התצוגה. לדוגמה, בהדגמה של הרשימה, ה-CSS מוסיף רק שמות לפריטים ברשימה:

ul li {
  view-transition-name: match-element;
  view-transition-class: album;
}

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

הדגמה חיה

הקלטת הדגמה

לצורך השוואה, הנה מבנה פסאודו-העץ של ההדגמה הזו עם השתתפות עצמית:

html
  ├─ head
  └─ body
     ├─ ul
     │  ├─ ::view-transition
     │  │  └─ ::view-transition-group(root)
     │  │     ├─ ::view-transition-group-children(root)
     │  │     │  ├─ ::view-transition-group(item1)
     │  │     │  │  └─ ::view-transition-image-pair(item1)
     │  │     │  │     ├─ ::view-transition-old(item1)
     │  │     │  │     └─ ::view-transition-new(item1)
     │  │     │  ├─ ::view-transition-group(item2)
     │  │     │  │  └─ …
     │  │     │  …
     │  │     └─ ::view-transition-image-pair(root)
     │  │        ├─ ::view-transition-old(root)
     │  │        └─ ::view-transition-new(root)
     │  ├─ li
     │  ├─ li
     │  └─ li
     └─ button#reorder

מכיוון שרכיב השורש של המעבר, <ul>, חותך את התוכן שלו בצורה אנכית, גם הרכיב ::view-transition-group-children(root) חותך את התוכן באופן אוטומטי.

מעברים בין תצוגות בהיקף של רכיב

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

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

הדגמה חיה

הקלטת הדגמה

הבידוד הזה מאפשר גם לעשות שימוש חוזר בערכי view-transition-name בהיקפים שונים. כל עוד השם ייחודי בהיקף שלו, לא תהיה התנגשות.

מעברים בין תצוגות בהיקף של רכיב מקונן והכללה של view-transition-name

כשעצי ה-DOM של כמה מעברי תצוגה בהיקף רכיב חופפים, יש סיכון של התנגשות ערכים של view-transition-name. לכן, הדפדפן מקצה אוטומטית את הערך view-transition-scope: all למעברי תצוגה פעילים בהיקף של רכיב, כדי לצמצם את הסיכון הזה.

בדומה לאופן שבו anchor-scope מגדיר את היקף הערכים של anchor-name, המאפיין view-transition-scope מבטיח שהיקף הערכים של view-transition-name יוגדר לעץ המשנה של האלמנט. המאפיין מקבל את הערך none, רשימה של שמות שרוצים להגדיר להם היקף, או all כדי להגדיר היקף לכל הערכים.

בנוסף למניעת חשיפה של שמות, view-transition-scope מונע גם את הלכידה של רכיב והתוכן שלו על ידי מעבר תצוגה חיצוני ובו-זמני. במהלך תהליך יצירת תמונת המצב, כשהמערכת עוברת על עץ המשנה כדי למצוא רכיב ליצירת תמונת מצב, היא מתעלמת מרכיבים (ומעץ המשנה כולו שלהם) שהוחל עליהם view-transition-scope: all. ההנחה היא שהרכיבים האלה כבר משתתפים במעבר תצוגה אחר בהיקף רכיב.

ההדגמה הבאה היא וריאציה של ההדגמה הקודמת. בנוסף לשני הלחצנים שמערבבים את תוכן הרשימה, יש גם לחצן החלפה להחלפת הרשימות. החלפה מתבצעת על ידי מעבר בין .reversed כיתות ב-#lists-wrapper.

הדגמה חיה

הקלטת הדגמה

מכיוון שהאפקט view-transition-scope: all מופעל אוטומטית במהלך מעבר ההחלפה, אפשר להתחיל מעבר החלפה חיצוני מקביל בזמן שמעבר ההחלפה עדיין מתבצע.

בנוסף, view-transition-scope: all מונע גם את צילום התמונה של רכיב במעבר חיצוני, ולכן בהדגמה נוספים גם ערכי view-transition-name לרכיבי ה-Wrapping של רכיבי <ul>.

#list1-wrapper, #list2-wrapper {
  view-transition-name: attr(id type(<custom-ident>));
}

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

html
  ├─ head
  └─ body
     └─ #lists-wrapper.reversed (SCOPE)
        ├─ ::view-transition
        │  └─ ::view-transition-group(lists-wrapper)
        │     ├─ ::view-transition-group-children(lists-wrapper)
        │     │  ├─ ::view-transition-group(list1-wrapper)
        │     │  │  └─ ::view-transition-image-pair(list1-wrapper)
        │     │  │     ├─ ::view-transition-old(list1-wrapper)
        │     │  │     └─ ::view-transition-new(list1-wrapper)
        │     │  └─ ::view-transition-group(list2-wrapper)
        │     │     └─ ::view-transition-image-pair(list2-wrapper)
        │     │        ├─ ::view-transition-old(list2-wrapper)
        │     │        └─ ::view-transition-new(list2-wrapper)
        │     └─ ::view-transition-image-pair(lists-wrapper)
        │        ├─ ::view-transition-old(lists-wrapper)
        │        └─ ::view-transition-new(lists-wrapper)
        ├─ div#list1-wrapper
        │  ├─ ul
        │  │  ├─ li#item1
        │  │  ├─ li#item2
        │  │  └─ li#item3
        │  └─ button.reorder
        └─ div#list2-wrapper
           ├─ ul (SCOPE)
           │  ├─ ::view-transition
           │  │  └─ ::view-transition-group(list)
           │  │     ├─ ::view-transition-group-children(list    )
           │  │     │  ├─ ::view-transition-group(item4)
           │  │     │  │  └─ ::view-transition-image-pair(item4)
           │  │     │  │     ├─ ::view-transition-old(item4)
           │  │     │  │     └─ ::view-transition-new(item4)
           │  │     │  ├─ ::view-transition-group(item5)
           │  │     │  │  └─ …
           │  │     │  …
           │  │     └─ ::view-transition-image-pair(list)
           │  │        ├─ ::view-transition-old(list)
           │  │        └─ ::view-transition-new(list)
           │  ├─ li#item4
           │  ├─ li#item5
           │  └─ li#item6
           └─ button.reorder

מידע נוסף

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