EduFlow

الوحدة 2 · HTML الدلالي أولًا

الأزرار والروابط والـ div القابل للنقر

17 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

اختر بشكل صحيح بين الرابط والزر، واستبدل عناصر div القابلة للنقر وروابط href=“#” بعناصر أصلية تعمل لمستخدمي لوحة المفاتيح وقارئ الشاشة.

  1. 1الروابط تنقلك، والأزرار تفعل
  2. 2ما الذي ينقص الـ div القابل للنقر
  3. 3الأزرار في النماذج، والأزرار المعطّلة
  4. 4نص مفهوم وحده

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

يبدو عنصر “Add to basket” في Ticketly كأنه زر: زوايا مستديرة، ولون العلامة التجارية، ومؤشّر يد عند التمرير فوقه. لكنه div عليه onclick. انقره بالفأرة فيعمل بإتقان، ولهذا بالضبط لم ينتبه أحد في الفريق الأصلي. اضغط Tab فيقفز التركيز متجاوزًا إياه. يقرؤه قارئ شاشة سمير كنص عادي. ومستخدم جهاز التبديل لا يستطيع الوصول إليه أصلًا.

الروابط تنقلك، والأزرار تفعل

الاختيار بين عنصري التحكّم الأصليين يعود إلى سؤال واحد: هل يأخذ المستخدم إلى مكان ما؟

  • الرابط (<a href="…">) ينقلك: إلى صفحة أخرى، أو جزء آخر من هذه الصفحة (#lineup)، أو ملف، أو عنوان بريد إلكتروني. تُعلنه قارئات الشاشة على أنه “link”. ويتوقّع المستخدمون أن يفتحوه في تبويب جديد، وأن ينسخوا عنوانه، وأن يروه في سجلّ التصفّح. ويُفعَّل بمفتاح Enter.
  • الزر (<button>) ينفّذ فعلًا في الصفحة الحالية: إضافة إلى السلة، أو فتح نافذة حوار، أو إرسال نموذج، أو تشغيل فلتر وإيقافه. تُعلنه قارئات الشاشة على أنه “button”. ويُفعَّل بمفتاحي Enter وSpace.
مسار القرار: إن كان تفعيل العنصر يغيّر عنوان URL أو ينقل إلى مكان آخر، فاستخدم عنصر a مع href؛ وإن كان يغيّر شيئًا في الصفحة الحالية، فاستخدم عنصر button؛ ولا تستخدم أبدًا div أو span عليه معالج نقر. هل يأخذ المستخدم إلى URL أو مكان جديد؟ نعم لا، يفعل شيئًا هنا <a href="…"> <button> أبدًا: div أو span عليه onclick
اختيار العنصر الصحيح

التنسيق لا علاقة له بالاختيار. يمكن أن يبدو الرابط كزر دعوة كبير، ويمكن أن يبدو الزر كرابط نصي؛ CSS يتولّى المظهر، والعنصر يتولّى السلوك.

ما الذي ينقص الـ div القابل للنقر

لتفهم لماذا يتفوّق العنصر الأصلي، عُدّ ما ينقص الـ div. هذا الحدّ الأدنى لإعادة بناء زر يدويًا:

HTML
<div class="btn" role="button" tabindex="0">Add to basket</div>
JavaScript
const fake = document.querySelector('.btn');
fake.addEventListener('click', addToBasket);
fake.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') addToBasket();
  if (event.key === ' ') event.preventDefault(); // stop the page scrolling
});
fake.addEventListener('keyup', (event) => {
  if (event.key === ' ') addToBasket(); // native buttons fire on Space release
});

هذا دور، وتركيز، وEnter، وSpace، ومنع للتمرير، ومع ذلك ما زالت تنقصه حالة التعطيل، وإرسال النموذج، والطريقة التي تنسّق بها سمات التباين في Windows (وضع forced colors) الأزرار. قارن ذلك بـ:

HTML
<button type="button" class="btn" onclick="addToBasket()">Add to basket</button>

عنصر واحد، وكل سلوك صحيح في كل متصفّح وكل قارئ شاشة. هذا هو النمط الذي ستراه على طول الدورة: HTML الأصلي أولًا، لأن المتصفّح أنجز العمل الصعب سلفًا.

الأزرار في النماذج، والأزرار المعطّلة

داخل <form>، يكون <button> افتراضيًا type="submit". في صفحة الدفع في Ticketly زر “Apply promo code” كان يُرسل النموذج كله مرة بعد مرة. والإصلاح خاصية واحدة:

HTML
<button type="button" onclick="applyPromo()">Apply promo code</button>

الخاصية disabled تُخرج الزر من ترتيب التنقّل بمفتاح Tab، فيتخطّاه مستخدمو لوحة المفاتيح وهم يتنقّلون في النموذج، ولا يُخبَر أحد لماذا هو غير متاح. صحيح أن قارئ الشاشة في وضع التصفّح (browse mode) يجده ويُعلنه “unavailable”، لكن دون أي سبب. لزر “Pay now” الذي ينتظر بطاقة صالحة، فكّر في إبقائه مفعّلًا وشرح المشكلة حين يُضغط، وهو ما ستبنيه في درس نماذج سهلة الوصول.

نص مفهوم وحده

كثيرًا ما يستدعي مستخدمو قارئ الشاشة قائمة بكل الروابط أو الأزرار في الصفحة، ومستخدمو التحكّم الصوتي ينطقون النص المرئي لينقروه. “Read more” و"Click here" و"Details" بلا معنى في تلك القائمة. يطلب معيار WCAG ‏2.4.4 أن يكون غرض الرابط واضحًا من نصه، أو من نصه مع الجملة أو عنصر القائمة المحيط به. واطمح إلى ما هو أعلى من الحدّ الأدنى: اكتب “Friday line-up and set times” لا “Read more”. وإن أصرّ التصميم على نص مرئي قصير، فستتعلّم في درس الأسماء والأوصاف المتاحة كيف توسّع الاسم دون أن تكسر التحكّم الصوتي.

وعادة أخرى مع الروابط: إن كان الرابط يفتح تبويبًا جديدًا أو يُنزّل ملفًا، فقل ذلك في نصه، مثل “Venue guide (PDF, 2 MB)” أو “Seating plan (opens in a new tab)”. التبويب الجديد غير المتوقّع يكسر زر الرجوع لمستخدمي قارئ الشاشة وبرامج التكبير، الذين كثيرًا ما لا يلاحظون أن النافذة تغيّرت فيتساءلون لماذا لا يستطيعون العودة.

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

أهم النقاط

  • الرابط يأخذ المستخدم إلى مكان ما (عنوان URL جديد أو جزء من الصفحة)؛ أما الزر فيفعل شيئًا في الصفحة الحالية.
  • الأزرار الأصلية قابلة للتركيز، وتُعلَن على أنها أزرار، وتُفعَّل بمفتاحي Enter وSpace، وكل ذلك مجانًا.
  • الـ div الذي عليه معالج نقر ليس له دور ولا تركيز ولا دعم للوحة المفاتيح؛ وإعادة بناء ذلك يدويًا تتطلّب أربعة إصلاحات منفصلة ويسهل أن تخطئ فيها.
  • داخل النماذج، أعطِ الأزرار التي لا تُرسل النموذج type=“button”، واكتب نصوصًا للروابط والأزرار تبقى مفهومة خارج سياقها.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

أيّ هذه العناصر يجب أن يكون رابطًا لا زرًّا؟

السؤال 2 من 4

«أصلح» أحد المطوّرين هذا الزر المزيّف. ما الذي ما زال ناقصًا؟

HTML
<div role="button" tabindex="0" onclick="addToBasket()">Add to basket</div>
السؤال 3 من 4

داخل نموذج الدفع في Ticketly، يُرسل زر «طبّق رمز الخصم» النموذج كله ويعرض «رقم البطاقة مطلوب». لماذا؟

السؤال 4 من 4

يستدعي مستخدم قارئ شاشة قائمة الروابط في Ticketly فيسمع “Read more, Read more, Read more”. ما أفضل إصلاح؟

تدريب

بيئة تجريب الكود

استبدل عناصر التحكّم المزيّفة في Ticketly

في بطاقة تذكرة الجمعة ثلاثة عناصر تحكّم، وليس أيٌّ منها العنصر الصحيح:

  • Add to basket عنصر div عليه onclick. يشغّل addToBasket().
  • Remove one عنصر <a href="#"> يشغّل removeFromBasket().
  • See the Friday line-up عنصر span يغيّر الـ hash في عنوان URL إلى #lineup.

أعد بناء كلٍّ منها بالعنصر الأصلي الصحيح، مع إبقاء النص المرئي كما هو تمامًا. يجب أن يظلّ الفعلان يحدّثان عدد التذاكر في السلة. ويمكنك الإبقاء على أسماء الكلاسات ليبقى التنسيق كما هو.

<p>Basket: <span id="basket-count">0</span> tickets</p>
<article class="pass">
  <h2>Friday pass</h2>
  <p>SAR 180, standing.</p>
  <div class="btn" onclick="addToBasket()">Add to basket</div>
  <a href="#" class="text-btn" onclick="removeFromBasket(); return false;">Remove one</a>
  <span class="link" onclick="location.hash = '#lineup'">See the Friday line-up</span>
</article>
<h2 id="lineup">Friday line-up</h2>
<p>Nour Quartet, The Saltmarsh Band.</p>جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • “Add to basket” زر حقيقي
  • نقر “Add to basket” يضيف تذكرة واحدة إلى عدد السلة
  • “Remove one” زر حقيقي يُنقص العدد
  • “See the Friday line-up” رابط ينتهي الـ href فيه بـ ‎#lineup
  • لم يبقَ أي معالج نقر على div أو span، ولا روابط بـ href=“#” أو بلا href