تشغيل انتقالات متزامنة ومتداخلة للعرض باستخدام انتقالات العرض على مستوى العنصر

تاريخ النشر: 27 مارس 2026

تتيح عمليات انتقال العرض المحدودة النطاق بالعناصر تشغيل عمليات انتقال عرض متعدّدة في الوقت نفسه، وتسمح بتضمين عمليات انتقال العرض الجارية في عملية أخرى، كما تحلّ المشاكل التي قد تواجهها في عمليات انتقال العرض المحدودة النطاق بالمستند، كل ذلك مع الحفاظ على تفاعلية بقية الصفحة.z-index اطّلِع على هذا الدليل لمعرفة كيفية استخدامها.

فيديو ترويجي: إعادة تصميم الويب باستخدام ميزة "انتقالات العرض المحدود النطاق" تجربة عرض توضيحي مباشر (الإصدار 147 من Chrome أو الإصدارات الأحدث)

الحاجة إلى انتقالات عرض مُفصَّل بنطاق أضيق

عند بدء انتقال العرض في المستند نفسه باستخدام 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. على الرغم من أنّ هذا الإجراء قد ينجح في حالة واحدة، إلا أنّه يصعب الحفاظ عليه ويؤدي إلى إجهاد عملية أخذ اللقطات بشكل غير ضروري.

عمليات نقل العرض على مستوى العنصر

تتيح لك عمليات انتقال العرض التي يتم تحديد نطاقها على مستوى العناصر بدء عملية انتقال عرض على شجرة فرعية من نموذج العناصر في المستند. بدلاً من استدعاء 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>، تبدو شجرة نموذج العناصر في المستند على النحو التالي أثناء نشاط انتقال العرض:

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 على الحجم والشكل نفسهما للعنصر الجذر للانتقال، ولا يتم عرضه إلا فوق العنصر الجذر للانتقال. لهذا السبب، يتم احترام ترتيب الطبقات للعناصر خارج جذر الانتقال.

على سبيل المثال، إذا كان لديك عنصر منبثق مرئي فوق العنصر <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;
}

جرِّب ذلك في العرض التوضيحي التالي. يتيح لك ذلك إلغاء المشاركة الذاتية. عندما يكون النطاق ذاتي المشاركة (السلوك التلقائي)، يعمل كل شيء على النحو المتوقّع. عندما لا يكون النطاق مشاركًا ذاتيًا، يتغيّر حدوده على الفور، وتتسرّب محتوياته من الغلاف أثناء الانتقال.

عرض توضيحي مباشر

تسجيل العرض التوضيحي

للمراجعة، يبدو المخطط الشجري الزائف لهذا العرض التوضيحي مع المشاركة الذاتية على النحو التالي:

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) أيضًا عملية اقتطاع تلقائيًا.

عمليات انتقال العرض المتزامنة على مستوى العنصر

بما أنّ عمليات الانتقال بين طرق العرض التي يقتصر نطاقها على العناصر يتم تنفيذها بشكل مستقل، يمكن تنفيذ عدّة عمليات انتقال بين طرق العرض التي يقتصر نطاقها على العناصر في الوقت نفسه إذا كان لها نطاق مختلف.

يحتوي العرض التوضيحي التالي على زرّين لإعادة الترتيب، أحدهما لكل قائمة. يبدأ كل زرّ انتقال عرض نطاقه عنصر في القائمة المعنيّة فقط. بما أنّ شجرتَي نموذج المستند لا تتداخلان، يمكن تشغيل انتقالَي العرض المحدودَين بالعناصر في الوقت نفسه بشكل منفصل.

عرض توضيحي مباشر

تسجيل العرض التوضيحي

تتيح لك هذه الطبيعة المعزولة أيضًا إعادة استخدام قيم view-transition-name في نطاقات مختلفة. وطالما أنّ الاسم يظل فريدًا ضمن نطاقه، لن يحدث أي تعارض.

عمليات الانتقال بين طرق العرض على مستوى العناصر المتداخلة واحتواء view-transition-name

عندما تتداخل أشجار DOM لعمليات انتقال العرض التي يتم تحديد نطاقها على مستوى عناصر متعدّدة، يكون هناك خطر من حدوث تعارض في قيمة view-transition-name. لهذا السبب، يحدّد المتصفّح تلقائيًا قيمة view-transition-scope: all لعناصر انتقالات العرض النشطة على مستوى العنصر من أجل الحدّ من هذا الخطر.

على غرار طريقة تحديد نطاق قيم anchor-name ضمن anchor-scope، تضمن السمة 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 إلى العناصر التي تتضمّن عناصر <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 وقائمة تعديلات المواصفات المفتوحة.