EduFlow

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

نماذج سهلة الوصول

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

الدرس في لمحة

ابنِ نموذج دفع لكل حقل فيه label، وخياراته المترابطة مجمّعة، ويستطيع المتصفّح تعبئته تلقائيًا، وأخطاؤه تُعلَن ويمكن إصلاحها دون الحاجة إلى البصر.

  1. 1لكل حقل label
  2. 2اجمع الخيارات المترابطة
  3. 3ساعد الناس والمتصفّحات على التعبئة
  4. 4أخطاء يستطيع الناس العثور عليها وإصلاحها

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

يخسر Ticketly ربع عملائه في صفحة الدفع، والتحليلات لا تستطيع أن تقول لماذا. جرّب النموذج بقارئ شاشة فيتّضح السبب: الحقول تُعلن “edit text, edit text, edit text”، وأزرار نوع التذكرة تُعلن “Standing, radio button” بلا سؤال، وحين يفشل الدفع يظهر إطار أحمر لا يدركه من لا يرى الشاشة. النماذج هي المكان الذي تتحوّل فيه إمكانية الوصول مباشرة إلى مال.

لكل حقل label

عنصر <label> المرتبط بحقل إدخال يفعل ثلاثة أشياء: يمنح الحقل اسمه المتاح، ويُظهر للمبصرين ما يجب أن يكتبوه حتى بعد أن يبدؤوا الكتابة، ويجعل نص الـ label قابلًا للنقر، فيكبر الهدف.

ticketly/checkout.html
<label for="full-name">Full name</label>
<input id="full-name" name="fullName" autocomplete="name">

<label>
  Email
  <input type="email" name="email" autocomplete="email" required>
</label>

الطريقتان تعملان: for تشير إلى id، أو حقل الإدخال متداخل داخل الـ label. ويمكنك التأكّد من الربط في الـ console: يجب أن تكون قيمة document.querySelector('#full-name').labels.length هي 1.

اجمع الخيارات المترابطة

لأزرار الـ radio ومربّعات الاختيار طبقتان من المعنى: كل خيار وحده، والسؤال الذي تجيب عنه معًا. يحمل <fieldset> و<legend> السؤال:

HTML
<fieldset>
  <legend>Ticket type</legend>
  <label><input type="radio" name="ticket" value="standing"> Standing</label>
  <label><input type="radio" name="ticket" value="seated"> Seated</label>
  <label><input type="radio" name="ticket" value="vip"> VIP lounge</label>
</fieldset>

حين يدخل التركيز المجموعة، يقرأ NVDA: “Ticket type, grouping, Standing, radio button, not checked, 1 of 3”. استخدم النمط نفسه لكتلة عنوان الفوترة أو لمجموعة مربّعات «أبلغني عبر».

ساعد الناس والمتصفّحات على التعبئة

خاصيتان توفّران كثيرًا من الكتابة، وهذا أهمّ ما يكون لذوي الإعاقات الحركية أو صعوبات الذاكرة.

الـ type الصحيح يُظهر لوحة المفاتيح المناسبة على الهواتف ويوفّر تحقّقًا أساسيًا: type="email"، وtype="tel"، وinputmode="numeric" لرقم البطاقة.

والخاصية autocomplete تعلن الغرض من الحقل، فيستطيع المتصفّح تعبئته من البيانات المحفوظة. يشترطها معيار WCAG ‏1.3.5 Identify Input Purpose ‏(AA) للحقول التي تخصّ المستخدم. من القيم الشائعة: name، given-name، family-name، email، tel، street-address، postal-code، country، cc-name، cc-number، cc-exp، cc-csc، username، current-password، new-password.

علِّم الحقول المطلوبة بخاصية required، التي تُعلنها قارئات الشاشة، وبنص مرئي مثل “(required)”، لا بنجمة فقط. ولا تسأل مرتين: إن كتب المستخدم بريده في الخطوة 1، فاملأه مسبقًا في الخطوة 3 (3.3.7 Redundant Entry).

أخطاء يستطيع الناس العثور عليها وإصلاحها

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

يمنح عنصر label حقلَ الإدخال اسمه عبر الخاصية for. ويحمل الحقل aria-invalid بقيمة true، وتشير aria-describedby إلى تلميح ورسالة خطأ يصبحان وصفه. وينتقل التركيز إلى الحقل بعد إرسال فاشل. <label for> <input> aria-invalid="true" نص التلميح رسالة الخطأ الاسم الوصف التركيز يصل هنا عند الخطأ
أجزاء الحقل سهل الوصول
  1. قل أيّ حقل خاطئ، بالنص (3.3.1 Error Identification).
  2. قل كيف يُصلَح (3.3.3 Error Suggestion): “Enter an email address like name@example.com”، لا “Invalid input”.
  3. اربطه برمجيًا: aria-invalid="true" على الحقل وaria-describedby تشير إلى الرسالة.
  4. انقل التركيز إلى أول حقل غير صالح (أو إلى ملخّص أخطاء في الأعلى في النماذج الطويلة) ليصل المستخدم إلى المشكلة مباشرة.
HTML
<label for="email">Email</label>
<input id="email" type="email" autocomplete="email" required>
<p id="email-error" class="error" hidden></p>
ticketly/checkout.js
const form = document.querySelector('#checkout');
const email = form.querySelector('#email');
const error = form.querySelector('#email-error');

form.addEventListener('submit', (event) => {
  if (email.value.includes('@')) {
    email.removeAttribute('aria-invalid');
    email.removeAttribute('aria-describedby');
    error.hidden = true;
    return;
  }
  event.preventDefault();
  error.textContent = 'Enter an email address like name@example.com';
  error.hidden = false;
  email.setAttribute('aria-invalid', 'true');
  email.setAttribute('aria-describedby', 'email-error');
  email.focus();
});

يستخدم النموذج novalidate في كود Ticketly ‏(<form id="checkout" novalidate>) لتحلّ هذه الرسالة المخصّصة محلّ الفقاعة المدمجة في المتصفّح، التي تختلف من متصفّح لآخر وتختفي بسرعة. هذا اختيار شائع؛ والإبقاء على التحقّق الأصلي مقبول أيضًا إن كانت رسائله ترضيك.

وفي النموذج الطويل الذي فيه عدة أخطاء، أضف ملخّص أخطاء في الأعلى: عنوانًا مثل “There are 2 problems”، تتبعه قائمة روابط، كلٌّ منها يشير إلى الحقل الذي يصفه (<a href="#email">Enter an email address</a>). وانقل التركيز إلى الملخّص بعد إرسال فاشل. يسمع المستخدمون عدد المشكلات، ويأخذهم كل رابط مباشرة إلى حقل.

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

صارت نماذج Ticketly تتكلّم بوضوح. والجزء الأخير من القسم 2 يتناول محتوى ليس نصًا على الإطلاق: الصور والأيقونات وجداول البيانات في صفحة الأسعار.

أهم النقاط

  • كل حقل إدخال يحتاج إلى label برمجي؛ والـ placeholder يختفي عند الكتابة وليس اسمًا يمكن الاعتماد عليه.
  • اجمع أزرار الاختيار (radio) ومربّعات الاختيار (checkbox) في fieldset يطرح الـ legend فيه السؤال.
  • قيم autocomplete مثل name وemail وcc-number تتيح للمتصفّحات تعبئة الحقول وتحقّق معيار 1.3.5 Identify Input Purpose.
  • عند الخطأ، اضبط aria-invalid، واربط الرسالة بـ aria-describedby، واشرح كيف يُصلَح الخطأ، وانقل التركيز إلى أول حقل غير صالح.

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

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

ماذا يُعلن قارئ الشاشة حين يصل التركيز إلى هذا الحقل؟

HTML
<input type="email" id="email" placeholder="Email">
السؤال 2 من 4

يسأل Ticketly عن «نوع التذكرة» بثلاثة أزرار radio. كيف يجب أن يُكتب السؤال؟

السؤال 3 من 4

بعد محاولة إرسال فاشلة، أيّ تغيير يفيد مستخدم قارئ الشاشة أكثر من غيره؟

السؤال 4 من 4

أيّ قيمة autocomplete صحيحة لاسم حامل البطاقة في خطوة الدفع في Ticketly؟

تدريب

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

أصلح نموذج الدفع في Ticketly

يعتمد نموذج الدفع على الـ placeholders، ويضع سؤالًا عائمًا فوق أزرار radio بلا labels، ويعرض خطأ البريد الإلكتروني بطريقة لا يلاحظها إلا المبصرون. أصلحه:

  1. أعطِ #full-name و#email وزرّي الـ radio عنصر <label> حقيقيًا (“Full name”، “Email”، “Standing”، “Seated”).
  2. اجمع أزرار الـ radio في <fieldset> يكون الـ <legend> فيه “Ticket type”.
  3. أضف autocomplete="name" وautocomplete="email"، واجعل حقل البريد type="email".
  4. في السكربت، حين يكون البريد غير صالح: اضبط aria-invalid="true"، واجعل aria-describedby تشير إلى #email-error، وانقل التركيز إلى حقل البريد. وحين يصبح صالحًا، احذف الخاصيتين.
<form id="checkout" novalidate>
  <input id="full-name" name="fullName" placeholder="Full name">
  <input id="email" name="email" type="text" placeholder="Email">
  <p id="email-error" class="error" hidden></p>

  <p class="question">Ticket type</p>
  <input type="radio" name="ticket" value="standing" id="t-standing"> Standing
  <input type="radio" name="ticket" value="seated" id="t-seated"> Seated

  <button id="pay">Pay now</button>
</form>جارٍ تحميل المحرّر…

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

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • لحقول الإدخال الأربعة labels بالنص الصحيح
  • أزرار الـ radio داخل fieldset أول عنصر ابن فيه هو الـ legend “Ticket type”
  • لحقلي الاسم والبريد قيم autocomplete، وحقل البريد type=“email”
  • البريد غير الصالح يضبط aria-invalid ويربط رسالة الخطأ المرئية بـ aria-describedby
  • ينتقل التركيز إلى حقل البريد بعد المحاولة الفاشلة
  • البريد الصالح يحذف aria-invalid ويُخفي رسالة الخطأ