القائمة المنسدلة
Dropdown / Select<select> / role="listbox"
اسمه كماندروب داون، سيلكت، قائمة الاختيار، اللائحة المنسدلة، حقل الاختيار، منسدلة، select، select box، picker، option list، listbox، select menu
القائمة المنسدلة زرّ يعرض قيمة واحدة مختارة، وعند فتحه تنبثق لائحة خيارات تُبدّل تلك القيمة ثم تُغلق. صفتها الجوهرية أنها تحمل قيمة (value): القيمة تبقى ظاهرة على الزرّ بعد الإغلاق، وتُرسَل مع النموذج، ويمكن قراءتها لاحقاً. ولا تخلطها بثلاثة أشياء تشبهها شكلاً. الأوّل الـ combobox القابل للكتابة، يضع حقل <input> محرّراً مكان الزرّ ويُصفّي اللائحة مع كل حرف ويعلن ذلك بـ aria-autocomplete="list". والفيصل بينهما ليس الدور، لأن ARIA 1.2 تُلبس المشغّلَين كليهما role="combobox"، بل هو وجود حقل كتابة من عدمه، ومن هنا بالضبط يأتي الخلط. والثاني القائمة (menu)، عنصر بـ role="menu" وبنوده role="menuitem" تُطلق أوامر مثل «نسخ» و«حذف» ولا تحتفظ بشيء، ولذلك لا وجود فيها لـ aria-selected أصلاً. والثالث الـ popover، مجرّد صندوق عائم بلا أي دلالة اختيار ولا علاقة ملكية بين حاويته وما بداخلها. والفيصل بجملة واحدة: لو الضغط على السطر ينفّذ فعلاً فهذه قائمة أوامر، ولو غيّر قيمة ستُقرأ لاحقاً فهذه قائمة اختيار. وداخل قوائم الاختيار نفسها يبقى فرق ثانٍ: الـ <select> الأصلي يرسمه النظام ولا تكاد تُنسَّق خياراته، لكنه يأتي بإمكانية وصول كاملة مجاناً، أمّا اللائحة المخصّصة (role="listbox") فتُنسّق كما تشاء وتتحمّل أنت كامل عبء لوحة المفاتيح و ARIA.
لو قلت عليه…
العيّنة الحيّة
تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.
تشريح العنصر: كل جزء واسمه
مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.
برومبت البناء
سلوكه في الاتجاه من اليمين لليسار
الجزء ده حصري عندنا.
محاذاة اللائحة
ينعكساللائحة تُحاذى بحافّة البداية المنطقية للمشغّل: يمينه في العربية، ويساره في الإنجليزية. فاكتبها inset-inline-start: 0 داخل حاوية position: relative بدل left: 0. ولو استعملت Floating UI فمحاذاة -start و -end فيها منطقية أصلاً وتقرأ direction من العنصر نفسه، فيكفي placement: "bottom-start" بلا أي شرط منك على الاتجاه. والفخّ الأكبر أن تُرسل اللائحة بـ portal إلى document.body: هناك تنقطع عن dir="rtl" الموروث من حاويتك العربية، فترتدّ محاذاة نصّها إلى اليسار، وتقرأ مكتبة التموضع الاتجاه ltr فتقلب المحاذاة كلها. عالجه بوضع dir على عنصر الـ portal نفسه أو على <html> مباشرة، لا على حاوية الصفحة وحدها.
السهم لا ينعكس
لا ينعكسسهم القائمة يشير إلى أسفل، والأسفل ليس له يمين ولا يسار. فلا تضع عليه scaleX(-1) ولا rtl:-scale-x-100 كما تفعل مع سهم مسار التنقّل. الحركة الوحيدة المسموحة له هي rotate(180deg) عند الفتح. وفي الـ <select> الأصلي يرسم النظام السهم بنفسه ويضعه على الحافّة النهائية تلقائياً حين تكون direction: rtl، فإن ألغيته بـ appearance: none وجب تثبيت سهمك أنت بـ inset-inline-end لا right.
داخل الخيار
ينعكسنصّ الخيار يُحاذى بـ text-align: start لا left، والمسافة المحجوزة لعلامة الصح تكون padding-inline-start. والفخّ الأشيع لائحة مختلطة مثل «الرياض» و«Riyadh (+966)»: لُفّ كل جزء لاتيني بـ <bdi> أو span بـ unicode-bidi: isolate، وإلا نقلت خوارزمية bidi علامة + والأقواس إلى الطرف الخطأ من السطر.
مفاتيح التنقّل الرأسية
لا ينعكسArrowUp و ArrowDown و Home و End لا تتأثّر باتجاه الصفحة إطلاقاً: Home أوّل خيار في اللائحة و End آخرها في العربية والإنجليزية سواء، لأن الترتيب هنا منطقي في الـ DOM لا بصري على الشاشة. لا تكتب شرطاً مثل if (dir === "rtl") لتبديلها، فهو يكسر توقّع المستخدم ويخالف نمط listbox في ARIA. وكذلك Escape يُغلق ويعيد التركيز إلى المشغّل بلا أي علاقة بالاتجاه.
الأسهم في اللائحة الأفقية
ينعكسإن نفّذت لائحة أفقية بـ aria-orientation="horizontal" فإن ArrowRight و ArrowLeft تتبادلان المعنى: في RTL يقودك ArrowLeft إلى الخيار التالي و ArrowRight إلى السابق. اقرأ الاتجاه مرّة واحدة من getComputedStyle(el).direction، واضرب خطوة المؤشّر في -1 عند rtl، بدلاً من ربط اسم المفتاح بالاتجاه مباشرة في كل مكان.
أسماؤه في الكود
كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.
شوف كمان
آخر تحديث · 2026-08-19