تاروپود — خودآموز HTML و CSS

فصل ۷ از ۱۰

پیشرفت ترم
۰٪

ترم ۱ · HTML: ساختار و معنا

جدول، فقط برای داده

فصل ۷پیش‌نمایش رایگان
۱۲ دقیقه مطالعه فصل ۷

در این فصل چه می‌سازی#

آخر این فصل یک جدول واقعی داری: ردهٔ چند تیم فوتبال محله، با ستون‌هایی برای برد و باخت و امتیاز، و یک عنوان بالای خودش. دقیقاً همان نوع جدولی که در سایت‌های خبری و ورزشی می‌بینی — نه یک عکس از یک جدول، بلکه جدولی که مرورگر همان لحظه از روی متنِ تو می‌سازد، ردیف‌به‌ردیف.

یک جدول ردهٔ لیگ با ستون‌های برد، باخت و امتیاز، همان‌طور که مرورگر نشانش می‌دهد

آخر این فصل می‌توانی:

  • یک <table> واقعی از داده بسازی، با ردیف (<tr>) و سلول
  • فرق <th> (سلولِ سر) و <td> (سلولِ داده) را بفهمی و درستش را انتخاب کنی
  • با scope به screen reader بگویی هر سلولِ سر مالِ یک ستون است یا یک ردیف
  • تشخیص بدهی چه چیزی داده است و چه چیزی نیست — و بگویی چرا <table> هرگز برای چیدمانِ صفحه به‌کار نمی‌رود

قبل از شروع#

  • از فصل ۱: اسکلت کامل صفحه (<!DOCTYPE html>، <head>، <body>، <title>).
  • از فصل ۲: تیتر و پاراگراف.

همین کافی است — و مثل همیشه فقط یک مرورگر لازم داری، یا همان آزمایشگاه.

۱. داده‌ای که ردیف و ستون دارد#

به ردهٔ یک لیگ فوتبال فکر کن. هر ردیف یک تیم است. هر ستون یک چیز است که دربارهٔ تیم‌ها می‌گویی: برد، باخت، امتیاز. حالا به یک خانهٔ وسط جدول نگاه کن، مثلاً جایی که ردیفِ «ستاره‌ها» و ستونِ «برد» به هم می‌رسند. آن عددْ معنایش را از هر دو می‌گیرد: بردهای ستاره‌ها. نه فقط از ردیف، نه فقط از ستون — از تقاطعشان.

این نکتهٔ کلیدی کل فصل است: وقتی یک اطلاعات هم‌زمان یک معنای ردیفی و یک معنای ستونی دارد، به آن داده‌ی جدولی می‌گویند، و این تنها موقعیتی است که سراغ <table> می‌روی.

<table> کل جدول را دربر می‌گیرد. داخلش، هر ردیف یک <tr> است (کوتاه‌شدهٔ «table row»). و داخل هر ردیف، هر خانه فعلاً یک <td> است («table data»). بیا یک نسخهٔ کوچک بسازیم. در آزمایشگاه این را بنویس:

<table>
  <tr>
    <td>تیم</td>
    <td>برد</td>
    <td>باخت</td>
  </tr>
  <tr>
    <td>ستاره‌ها</td>
    <td>4</td>
    <td>0</td>
  </tr>
  <tr>
    <td>شهاب‌ها</td>
    <td>3</td>
    <td>2</td>
  </tr>
</table>

چک کن: باید سه ردیف ببینی، و در هر ردیف سه خانه که مرتب زیر هم در ستون چیده شده‌اند. تو هیچ‌جا نگفتی «این‌ها را در ستون بچین» — فقط گفتی کدام <td> داخل کدام <tr> است، و مرورگر خودش ستون‌ها را ردیف کرد.

⚠️ مواظب باش: هر خانه حتماً باید داخل یک <tr> باشد. اگر یک <td> را بیرون از هر ردیفی رها کنی، جدول به‌هم می‌ریزد و خانه‌ها سرِ جای درستشان نمی‌نشینند. اول ردیف را باز کن، خانه‌هایش را بگذار، بعد ردیف را ببند.

۲. <th> در برابر <td>: سلولِ سر در برابر سلولِ داده#

به ردیف اول جدولِ بالا دقت کن: «تیم»، «برد»، «باخت». این‌ها داده نیستند — این‌ها اسمِ ستون‌ها هستند. ولی ما آن‌ها را هم با <td> نوشتیم، دقیقاً مثل عددها. نتیجه را نگاه کن: ردیف اول عیناً مثل ردیف‌های داده به‌نظر می‌رسد؛ همان ضخامت، همان چینش. هیچ‌چیز نمی‌گوید «برد» سرِ یک ستون است و «۴» یک مقدار زیر آن. معنا گم شده.

بیا درستش کنیم. فقط خانه‌های ردیف اول را از <td> به <th> عوض کن:

<tr>
  <th>تیم</th>
  <th>برد</th>
  <th>باخت</th>
</tr>

چک کن: حالا ردیف اول ضخیم‌تر و وسط‌چین شده، و از ردیف‌های داده جدا دیده می‌شود. <th> کوتاه‌شدهٔ «table header» است.

ولی مهم‌تر از پررنگ‌شدنش، معنایش است. باز همان درسِ همیشگیِ این خودآموز: هیچ‌جا نگفتیم «این را ضخیم کن». فقط گفتیم <th> یعنی «این سلول، سرِ ستون (یا ردیف) است». ضخیم و وسط‌چین بودنش تصمیم پیش‌فرض مرورگر بود. بعداً در ترم ۲ با CSS می‌توانی ظاهرش را عوض کنی — ولی معنای «سر بودن» سرِ جایش می‌ماند. یک screen reader هم دقیقاً به همین معنا تکیه می‌کند، نه به ضخامت متن.

⚠️ مواظب باش: وسوسه نشو که فقط برای ضخیم‌کردنِ یک خانه از <th> استفاده کنی، یا برعکس یک سرستونِ واقعی را با <td> بگذاری و بعد با CSS ضخیمش کنی. <th> یک ادعای معنایی است، نه یک ابزار ظاهری. اگر خانه سرِ ستون یا ردیف است، <th>؛ اگر یک مقدار معمولی است، <td>.

۳. <caption>: اسمِ خودِ جدول#

هر جدول لیاقت یک اسم دارد: این جدول دربارهٔ چیست؟ می‌توانی یک <p> یا <h2> بالای جدول بگذاری، ولی یک element درست‌تر وجود دارد که به خودِ جدول تعلق دارد: <caption>. این باید اولین چیز داخل <table> باشد، درست بعد از تگ باز.

<table>
  <caption>ردهٔ تیم‌ها تا پایان هفتهٔ پنجم</caption>
  <tr>
    <th>تیم</th>
    ...

چرا فقط یک <p> بالای جدول کافی نیست؟ چون <caption> به جدول گره خورده است. یک screen reader آن را همراهِ خودِ جدول اعلام می‌کند؛ یعنی کسی که نمی‌بیند، همان لحظه که به جدول می‌رسد اسمش را می‌شنود و می‌داند دارد چه چیزی را می‌خواند، قبل از اینکه عددها شروع شوند. یک <p> بالای جدول فقط یک پاراگراف است که اتفاقی آن نزدیکی نشسته؛ هیچ ربطِ رسمی‌ای به جدول ندارد.

چک کن: باید یک خط عنوان درست بالای جدول ببینی، معمولاً وسط‌چین.

🔧 اگر کار نکرد: عنوان بالای جدول ظاهر نشد یا جای عجیبی رفت؟ احتمالاً <caption> را جایی غیر از اولین خطِ داخل <table> گذاشته‌ای. باید بلافاصله بعد از <table> بیاید، قبل از هر <tr>.

۴. <thead> و <tbody>: جدا کردن سر از تنه#

الان ردیفِ سر و ردیف‌های داده همه به‌صورت <tr> های آزاد کنار هم‌اند. می‌توانی به مرورگر بگویی کدام قسمت سرِ جدول است و کدام تنهٔ آن:

  • <thead> ردیف (یا ردیف‌های) سر را دربر می‌گیرد.
  • <tbody> ردیف‌های داده را دربر می‌گیرد.
<table>
  <caption>ردهٔ تیم‌ها تا پایان هفتهٔ پنجم</caption>
  <thead>
    <tr>
      <th>تیم</th>
      <th>برد</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ستاره‌ها</td>
      <td>4</td>
    </tr>
  </tbody>
</table>

چرا زحمتش را بکشیم؟ دو دلیل، هر دو صادقانه:

  1. معنا: با صدای بلند می‌گویی «این تکه سر است، این تکه تنه». screen reader ها و ابزارهای دیگر از این تفکیک استفاده می‌کنند.
  2. کاربردی: وقتی یک جدولِ بلند روی چند صفحه چاپ می‌شود، مرورگر می‌داند <thead> همان سری است که باید بالای هر صفحه تکرار شود. و بعداً که به CSS برسی، استایل‌دادن به «همهٔ خانه‌های تنهٔ جدول» ساده می‌شود، چون تنه یک گروهِ نام‌دار است.

چک کن: ظاهر جدول تقریباً همان قبلی است — این تغییر بیشتر برای معنا و ابزارهاست تا برای چشم. اگر ردیفی جابه‌جا شد، احتمالاً یک <tr> را بیرون از <thead> یا <tbody> جا گذاشته‌ای.

۵. scope: به screen reader بگو این سر مالِ کجاست#

حالا ظریف‌ترین و مهم‌ترین بخش برای دسترس‌پذیری. یک خوانندهٔ بینا وقتی به عدد «۱۳» می‌رسد، چشمش را بالا می‌برد تا «امتیاز» و کنار می‌برد تا «ستاره‌ها»، و در یک لحظه می‌فهمد این امتیازِ ستاره‌هاست. کسی که با screen reader می‌خواند نمی‌تواند با چشم این خط‌ها را دنبال کند. scope همان ابزاری است که این خط‌ها را برای او می‌کشی.

روی هر سرِ ستون، scope="col" بگذار — یعنی «این سر، عنوانِ یک ستون است»:

<th scope="col">برد</th>

و حالا حرکتِ واقعاً مفید: اسم هر تیم هم در واقع داده نیست، بلکه عنوانِ کلِ آن ردیف است. پس اولین خانهٔ هر ردیف را هم <th> کن، ولی این‌بار سرِ ردیف، با scope="row":

<tr>
  <th scope="row">ستاره‌ها</th>
  <td>4</td>
  <td>1</td>
  <td>0</td>
  <td>13</td>
</tr>

حالا هر <td>ی معمولی درست سرِ تقاطعِ یک scope="col" و یک scope="row" نشسته. وقتی screen reader خانهٔ «۱۳» را می‌خواند، می‌تواند اعلام کند: «ستاره‌ها، امتیاز، ۱۳» — عنوانِ ردیف، عنوانِ ستون، و مقدار. آن یک عدد ناگهان همهٔ زمینه‌اش را با صدا پیدا می‌کند.

حالا جدولِ کامل را کنار هم بگذار. کل صفحه این است:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <title>جدول ردهٔ لیگ</title>
  </head>
  <body>
    <h1>جدول ردهٔ لیگ محله</h1>
    <table>
      <caption>ردهٔ تیم‌ها تا پایان هفتهٔ پنجم</caption>
      <thead>
        <tr>
          <th scope="col">تیم</th>
          <th scope="col">برد</th>
          <th scope="col">مساوی</th>
          <th scope="col">باخت</th>
          <th scope="col">امتیاز</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">ستاره‌ها</th>
          <td>4</td>
          <td>1</td>
          <td>0</td>
          <td>13</td>
        </tr>
        <tr>
          <th scope="row">شهاب‌ها</th>
          <td>3</td>
          <td>0</td>
          <td>2</td>
          <td>9</td>
        </tr>
        <tr>
          <th scope="row">عقاب‌ها</th>
          <td>1</td>
          <td>2</td>
          <td>2</td>
          <td>5</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

چک کن: به چشم، اسمِ تیم‌ها هم حالا کمی پررنگ‌تر شده — چون <th> شدند. ولی کارِ اصلیِ scope را با چشم نمی‌بینی؛ آن برای کسی است که با گوش می‌خواند. این یکی از معدود چیزهایی در این خودآموز است که اثرش نامرئی ولی واقعی است، و همین است که آن را از یک تزئین جدا می‌کند.

💡 نکته: برای بیشترِ جدول‌ها همین دو مقدار کافی است: scope="col" برای سرِ ستون‌ها و scope="row" برای سرِ ردیف‌ها. اگر جدولت این دو را درست داشته باشد، از نظر دسترس‌پذیری جلوترِ خیلی از سایت‌های واقعی هستی.

چالش: این جدول است یا سوءاستفاده؟

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

۶. قانونی که از همه مهم‌تر است: جدول فقط برای داده#

سال‌ها پیش، قبل از اینکه CSS ابزارهای واقعیِ چیدمان داشته باشد (که در ترم ۳ می‌بینی‌شان — flexbox و grid)، آدم‌ها از <table> برای چیدنِ کلِ صفحه استفاده می‌کردند: سرصفحه بالا، یک ستونِ کناری، محتوا وسط — همه به‌عنوان خانه‌های یک جدولِ بزرگ و اغلب تودرتو. روی صفحه خوب به‌نظر می‌رسید. ولی غلط بود، و دلیلِ ملموسش این است:

یک screen reader وقتی به <table> می‌رسد، آن را به‌عنوان داده اعلام می‌کند — «جدول، ۳ ردیف در ۴ ستون» — و شروع می‌کند خانه‌به‌خانه خواندنش، انگار عددها معنایی دارند. برای یک جدولِ دادهٔ واقعی این دقیقاً درست است. ولی برای صفحه‌ای که فقط تظاهر به جدول‌بودن می‌کرد، کسی که نمی‌بیند یک شبکهٔ بی‌معنا می‌شنود و گم می‌شود. تو ندانسته به او دربارهٔ اینکه محتوایت چه هست دروغ گفته‌ای.

چپ: یک صفحهٔ وب که کلِ چیدمانش به‌غلط با خانه‌های یک جدول ساخته شده و علامت خطا خورده؛ راست: همان صفحه با جعبه‌های جدا و درست و علامت تأیید

این کلِ قانون است، و مهم‌ترین جملهٔ این فصل: <table> یعنی «این داده‌ای است که در ردیف و ستون سازمان یافته». اگر چیزِ تو داده نیست — اگر فقط می‌خواهی چند جعبه کنار هم باشند — جدول نیست. کنارِ هم چیدنِ جعبه‌ها کارِ چیدمان است، و چیدمان ابزارهای درستِ خودش را دارد که در ترم ۳ یاد می‌گیری.

پس آزمونِ همیشگی، هر بار، یک سؤال است: «اگر این را برای کسی که فقط صدایش را می‌شنود توصیف کنم، واقعاً یک جدولِ داده است؟» اگر آره — caption و thead/tbody و th و scope، همه‌اش. اگر نه — از اول هم جدول نبود.

🤖 با AI: یک چیدمان را برای دستیار AI ات توصیف کن — مثلاً «می‌خواهم منوی بالای سایتم، یعنی چهار لینک کنارِ هم، را بچینم؛ آیا بهتر است از <table> استفاده کنم؟» — و جوابش را نگاه کن. گاهی AI با اعتمادبه‌نفس می‌گوید «بله، یک جدولِ یک‌ردیفه بساز». این دقیقاً همان‌جاست که باید بایستی: یک منو داده نیست، پس جدول نیست. جوابش را با معیارِ خودت بسنج — «این ردیف و ستونِ معنادار دارد؟» — و با چیزی که در آزمایشگاه می‌بینی چک کن. یادت باشد: AI هم اشتباه می‌کند، مخصوصاً وقتی پای چیدمان وسط است.

واژه‌های تازهٔ این فصل#

کلمه تلفظ به حروف فارسی یعنی چه
table تِیبِل جدول؛ فقط برای داده‌ای که ردیف و ستون دارد (<table>)
caption کَپشِن عنوانِ خودِ جدول، اولین چیز داخل <table> (<caption>)
table row تِیبِل رو یک ردیف جدول (<tr>)
header cell هِدِر سِل سلولِ سرِ ستون یا ردیف (<th>)
data cell دیتا سِل سلولِ دادهٔ معمولی (<td>)
scope اِسکوپ می‌گوید یک <th> سرِ ستون است (scope="col") یا ردیف (scope="row")

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

الان می‌توانی متن، پیوند، تصویر، فهرست و جدول بسازی — یعنی تقریباً هر جور محتوایی که فقط برای خواندن است. فصل بعد سراغ چیزی می‌رویم که کاربر با آن حرف می‌زند: فرم. یاد می‌گیری چطور با <form>، <input>، <label> و <button> از کاربر ورودی بگیری — از یک کادر جست‌وجوی ساده تا یک فرمِ ثبت‌نامِ کامل.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.