Semantic HTML در React: پایه دسترس‌پذیری

تا اینجا در این دوره، عمدتاً روی منطق و رفتار React تمرکز کردیم؛ کمتر به این پرداختیم که خروجی HTML نهایی، چقدر برای همه کاربران، از جمله کسانی که از صفحه‌خوان یا صفحه‌کلید استفاده می‌کنند، قابل استفاده است. این فصل به دسترس‌پذیری (Accessibility، به‌اختصار a11y) می‌پردازد؛ و این درس اول، با پایه‌ای‌ترین ابزار آن شروع می‌کند: استفاده درست از تگ‌های معنایی HTML.

مشکل: div همه‌کاره

چون JSX اجازه می‌دهد هر چیزی را با div بسازیم، یک الگوی رایج و مشکل‌ساز این است که کل رابط کاربری، صرف‌نظر از نقش واقعی هر بخش، با div و کمی CSS ساخته شود:

jsx
// نامناسب از نظر دسترس‌پذیری
function Page() {
  return (
    <div className="header">
      <div className="nav">
        <div onClick={handleClick}>خانه</div>
      </div>
    </div>
  );
}

از نظر بصری، این کد ممکن است دقیقاً شبیه یک هدر و منو به نظر برسد. اما یک صفحه‌خوان (Screen Reader)، که کاربران نابینا یا کم‌بینا از آن استفاده می‌کنند، هیچ اطلاعاتی درباره نقش این عناصر ندارد؛ از نگاه آن، این‌ها فقط چند div بی‌معنا و یک متن قابل کلیک هستند، نه یک هدر، یک منو، یا یک دکمه واقعی.

راه‌حل: تگ‌هایی که نقش را بیان می‌کنند

HTML مجموعه‌ای از تگ‌های معنایی دارد که خودشان نقش محتوای داخلشان را اعلام می‌کنند؛ این‌ها در React دقیقاً مثل هر عنصر دیگر در JSX نوشته می‌شوند:

jsx
function Page() {
  return (
    <header>
      <nav>
        <button onClick={handleClick}>خانه</button>
      </nav>
    </header>
  );
}

با این تغییر، صفحه‌خوان می‌تواند مستقیماً اعلام کند «این یک ناوبری است» یا «این یک دکمه است»؛ و کاربری که با صفحه‌کلید کار می‌کند، می‌داند button را می‌تواند با کلید Tab پیدا کند و با Enter یا Space فعال کند، بدون هیچ کد اضافه‌ای از طرف ما.

چرا button بهتر از div با onClick است

این مقایسه، یکی از رایج‌ترین نمونه‌های این موضوع است. یک div با onClick، از نظر بصری می‌تواند دقیقاً شبیه یک دکمه باشد، اما به‌طور پیش‌فرض: در ترتیب Tab صفحه‌کلید قرار نمی‌گیرد، با کلید Enter یا Space فعال نمی‌شود، و به صفحه‌خوان اعلام نمی‌کند که قابل کلیک است. تگ button، همه این رفتارها را به‌طور رایگان و بدون کد اضافه، از مرورگر دریافت می‌کند.

چند تگ معنایی پرکاربرد دیگر

  • <main>: محتوای اصلی و منحصربه‌فرد صفحه.
  • <nav>: بخش‌های ناوبری.
  • <article>: یک قطعه محتوای مستقل (مثل یک پست وبلاگ).
  • <ul>/<li>: برای هر لیستی از آیتم‌های مشابه، همان‌طور که در فصل اول هنگام رندر لیست‌ها دیدیم؛ نه یک سری div پشت سر هم.

یک قانون عملی ساده

پیش از نوشتن div یا span برای چیزی، این سؤال را بپرسید: آیا HTML از قبل یک تگ با همین معنا دارد؟ اگر پاسخ بله است (دکمه، لیست، ناوبری، عنوان)، از همان تگ معنایی استفاده کنید. div و span را برای مواقعی نگه دارید که واقعاً هیچ معنای خاصی ندارند و فقط برای گروه‌بندی بصری لازم‌اند.

این موضوع، هزینه اضافه‌ای در React ندارد

نکته خوب این است که استفاده از تگ معنایی به‌جای div، هیچ هزینه عملکردی یا پیچیدگی اضافه‌ای در React ندارد؛ فقط انتخاب یک نام تگ متفاوت در JSX است. مقاومت در برابر این تغییر، معمولاً فقط از عادت می‌آید، نه یک محدودیت واقعی فنی.

مثال قابل اجرا

جایگزینی div همه‌کاره با تگ‌های معنایی
function Page() {
  return (
    <div>
      <header>
        <nav>
          <button onClick={() => alert("رفتن به خانه")}>خانه</button>
          <button onClick={() => alert("رفتن به محصولات")}>محصولات</button>
        </nav>
      </header>
      <main>
        <article>
          <h2>عنوان مقاله</h2>
          <p>این محتوای اصلی صفحه است.</p>
        </article>
      </main>
    </div>
  );
}
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

استفاده از div برای هر نوع محتوا، نقش واقعی هر بخش را از صفحه‌خوان و از ابزارهای مبتنی بر صفحه‌کلید پنهان می‌کند. تگ‌های معنایی مثل header، nav و به‌خصوص button به‌جای div با onClick، این رفتارها را به‌طور رایگان از مرورگر می‌گیرند. در درس بعدی، به دسترس‌پذیری فرم‌ها و اهمیت Label می‌پردازیم.