القوائم المنسدلة المتوافقة مع إطار العرض

تُعدّ لوحة الإجراءات التي يتم الكشف عنها أو مجموعة أزرار النوافذ المنبثقة نمطًا مفيدًا للمستخدمين للوصول إلى وظائف إضافية مع شغل الحد الأدنى من المساحة. يأتي نمط التراكب هذا مع تعقيد في التخطيط، حيث يجب أن تظل اللوحة مرتبطة بعنصر مشغّل أثناء التكيف مع قيود إطار العرض. في السابق، كان ذلك يتطلّب مكتبات JavaScript معقّدة (مثل Popper.js أو Floating UI) لحساب المواضع ومعالجة التداخلات.

توفّر ميزة "تحديد موضع الارتساء في CSS" طريقة تعريفية ومحسّنة للأداء للتعامل مع هذه العلاقات بالكامل في CSS، ما يتيح للمتصفّحات إدارة منطق تحديد الموضع والتجاوز بشكلٍ أصلي.

1. تحديد العلاقة بين الزر واللوحة

الخطوة الأولى هي إنشاء زر تشغيل يفتح حاوية التراكب باستخدام Popover API.

MANDATORY Accessibility Distinction: This pattern explicitly models a button group revealed inside a popover rather than a true ARIA menu. لا تطبِّق role="menu" أو role="menuitem" إلا إذا نفّذت عقد التنقّل باستخدام لوحة المفاتيح بالكامل (مثل التعامل مع التنقّل المكاني باستخدام مفاتيح الأسهم بين العناصر). للسبب نفسه، لا تضيفوا aria-haspopup إلى المشغّل، لأنّ قيمته (menu أو listbox أو tree أو grid أو القيمة القديمة true) هي وعد بأنّ الهدف يعرض دورًا مطابقًا، وهو ما لا يفعله هذا النمط.

<button popovertarget="action-panel">
  Open Actions
</button>
<!-- Use the Popover API (`popover="auto"`) for the overlay to ensure it is placed in the top layer and handled accessibly by the browser. -->
<div id="action-panel" popover="auto" class="panel">
  <button class="action-item" type="button">Edit</button>
</div>

يؤدي ذلك إلى إنشاء ارتباط إرساء ضمني بين الزر واللوحة، بحيث يمكن تحديد موضع اللوحة بالنسبة إلى الزر بشكل أصلي بدون الحاجة إلى anchor-name على الزر أو position-anchor على اللوحة.

2. تحديد الموقع الجغرافي باستخدام position-area

بدلاً من استخدام الإزاحات اليدوية top/left، استخدِم position-area لوضع الهدف على شبكة مربّعات ثلاثية بالنسبة إلى نقطة الارتكاز.

.panel {
  /* 
     Position the panel below the anchor (block-end), 
     aligned to the start of the anchor and spanning to its end (span-inline-end).
  */
  position-area: block-end span-inline-end;
  
  /* Reset insets to allow the grid to take control */
  inset: auto;
}

يُفضَّل استخدام الكلمات الرئيسية المنطقية (span-inline-end وblock-start) بدلاً من الكلمات الرئيسية المادية (left وtop) لإتاحة الكتابة من اليمين إلى اليسار وأوضاع الكتابة المختلفة تلقائيًا.

إلزامي: لا تخلط بين الكلمات الرئيسية المادية والمنطقية في position-area.

3- تنفيذ ميزة "المرونة في الحافة" (الإجراءات الاحتياطية)

لمنع اقتطاع اللوحة عند حافة الشاشة، حدِّد "أساليب المحاولة" التي يجب أن يحاول المتصفّح تنفيذها في حال تجاوز الموضع التلقائي.

.panel {
  /* 
     If the panel overflows the bottom, flip it to the top (flip-block).
     If it overflows the inline edges, flip it horizontally (flip-inline).
     If it overflows both (e.g., in a corner), flip on both axes (flip-block flip-inline).
  */
  position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
}

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

استراتيجيات احتياطية

الخيارات الاحتياطية وتوافق المتصفّحات مع Popover

تعتمد واجهة Popover API في الغالب على التحسين التدريجي، ولكن سلوكياتها المحدّدة، مثل الترقية إلى الطبقة العليا والإغلاق البسيط واستدعاء popovertarget، ليس لها مكافئ في CSS فقط. تحتاج المتصفّحات القديمة إلى polyfill أو إلى إجراء احتياطي يدوي إذا كنت تفضّل عدم شحنها.

Polyfill: لإتاحة السمة popover في المتصفّحات القديمة، يمكنك تحميل @oddbird/popover-polyfill بشكل مشروط. إجراء إلزامي: يجب رصد الميزة من خلال التحقّق من السمة popover في HTMLElement.prototype، وتحميل polyfill فقط في حال عدم توفّر الدعم الأصلي، ويجب عدم تحميله بدون شروط.

باستخدام حزمة أو خريطة استيراد:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

بدون حزمة، يمكنك الاستيراد من شبكة توصيل محتوى (CDN) داخل <script type="module">:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

تحذير بشأن التنسيق: لا يمكن لـ polyfill تحديد الفئة الزائفة :popover-open الحقيقية، لذا يتم تطبيق فئة .\:popover-open بدلاً من ذلك. إجراء إلزامي: ادمج السمتَين مع :is() أو :where()، وإلا ستتجاهل المتصفحات التي لا تتضمّن :popover-open القاعدة بأكملها:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

بدلاً من ذلك، يمكنك استخدام position: fixed واحتساب الإحداثيات يدويًا من خلال getBoundingClientRect() أو الاعتماد على تحديد الموضع التلقائي باستخدام inset: auto إذا كان ذلك مقبولاً لحالة الاستخدام، وذلك كحل بديل قديم بدون polyfill.

لإتاحة المتصفّحات التي لا تتضمّن ميزة تحديد موضع الارتساء، يجب ضبط موضع معقول. By default popovers are centered in the middle of the screen, which may work for your use case.

في بعض حالات الاستخدام، قد تتمكّن من استخدام polyfill @oddbird/css-anchor-positioning الذي يضيف دعمًا لبعض حالات استخدام تحديد موضع الارتساء. لا يتيح هذا الإجراء استخدام نقاط الارتساء الضمنية، لذا يجب إضافة أسماء نقاط الارتساء إلى عامل التفعيل. بالإضافة إلى ذلك، لا يتيح polyfill استخدام position-area في النوافذ المنبثقة، لذا يجب استخدام anchor() في الأجزاء المطلوبة.

<!-- MANDATORY: Conditionally install the anchor positioning polyfill -->
<script type="module">
  if (!("anchorName" in document.documentElement.style)) {
    await import("https://unpkg.com/@oddbird/css-anchor-positioning");
  }
</script>
.panel {
  /* Mandatory: use explicit anchor name */
  position-anchor: --kebab-anchor;
  /* Mandatory: use insets rather than position-area for positioning */
  bottom: auto;
  right: auto;
  top: anchor(bottom);
  left: anchor(left);
  margin: 0;
}