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

فصل ۲ از ۱۰

پیشرفت ترم
۰٪

ترم ۵ · تعامل و جزئیات

selector های ساختاری و combinator ها

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

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

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

یک فهرستِ عمودی از چند ردیف که یک‌درمیان دو رنگِ روشن دارند؛ ردیفِ اول پررنگ و آبی، ردیفِ آخر با رنگِ متفاوت، و بینِ ردیف‌ها خطِ نازکِ جداکننده

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

  • با :nth-child و :first-child/:last-child عنصری را بر اساسِ جایگاهش انتخاب کنی
  • با :not() همه را جز یک استثنا هدف بگیری
  • با combinator های >، + و ~ رابطهٔ بینِ عنصرها را انتخاب کنی
  • با :is() و :where() چند selector را کوتاه و تمیز گروه کنی

قبل از شروع#

  • از ترم ۱: <ul> و <li> — کلِ کار روی یک فهرست است.
  • از ترم ۲: selector نوع و class، و specificity — آخرِ فصل دوباره به کارت می‌آید.
  • از ترم ۳: border-block-end و margin-inline — برای خط‌ها و فاصله‌های راست‌به‌چپ.

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

۱. انتخاب بر اساسِ جایگاه: :first-child و :last-child#

تا حالا برای اینکه یک آیتمِ خاص را استایل بدهی، به آن class می‌دادی. ولی گاهی نمی‌خواهی — یا نمی‌توانی — به هر آیتم دست بزنی. مثلاً می‌خواهی «اولین درس» همیشه برجسته باشد، هر درسی که باشد. برای این کار CSS چند selector دارد که به جایگاهِ عنصر نگاه می‌کنند، نه اسمش. این‌ها هم pseudo-class اند (مثلِ فصلِ قبل)، ولی به‌جای حالتِ تعاملی، جایگاهِ ساختاری را هدف می‌گیرند.

اولین‌ها ساده‌اند: :first-child یعنی «عنصری که اولین فرزندِ والدش است» و :last-child یعنی «آخرین فرزند». اول به فهرست یک استایلِ پایه بده، بعد اولی و آخری را جدا کن:

.lessons > li:first-child {
  font-weight: 700;
  color: #2a6b93;
}

.lessons > li:last-child {
  color: #bc3e1c;
}

چک کن: «مقدمه» (اولین درس) پررنگ و آبی شد، و «پروژهٔ پایانی» (آخرین) قرمز. حالا در HTML جای دو درس را عوض کن یا یکی را پاک کن — می‌بینی این استایل‌ها همان لحظه به آیتمِ تازه‌ای که سرِ آن جایگاه نشسته می‌پرند. تو جایگاه را هدف گرفتی، نه یک آیتمِ مشخص را.

۲. :nth-child(): یک الگوی جایگاهی#

:first-child فقط اولی را می‌گیرد. اگر بخواهی «هر آیتمِ زوج» یا «هر سومی» را بگیری، :nth-child() را داری: داخلِ پرانتزش یک الگو می‌نویسی. پرکاربردترین‌ها even (زوج) و odd (فرد) هستند. با even یک راه‌راهِ ملایم بساز تا فهرست خواناتر شود:

.lessons > li:nth-child(even) {
  background-color: #f0e7d2;
}

چک کن: حالا درس‌های دوم، چهارم و ششم یک پس‌زمینهٔ روشن گرفتند و فهرست راه‌راه شد. even را به odd عوض کن و ببین رنگ به سطرهای فرد می‌پرد.

اما اینجا یک تلهٔ کلاسیک هست که تقریباً همه یک‌بار توش می‌افتند. بیا عمداً توش بیفتیم تا خوب یادت بماند. فرض کن می‌خواهی بالای فهرست یک آیتمِ «نکتهٔ روز» اضافه کنی. در HTML، درست قبل از «مقدمه» این را بگذار:

<li>نکتهٔ روز: هر روز کمی تمرین کن</li>

حالا به فهرست نگاه کن: راه‌راه به‌هم ریخت و «مقدمه» — که می‌خواستی همیشه اولی و آبی باشد — دیگر پررنگ نیست! انگار selector هایت جابه‌جا شدند.

🔧 اگر کار نکرد: این دقیقاً درسِ همین بخش است. :nth-child و :first-child به جایگاه در میانِ همهٔ خواهر-برادرها نگاه می‌کنند، نه به class و نه به «چندمین آیتمِ نوعِ خاص». حالا «نکتهٔ روز» فرزندِ اول است، پس :first-child رفت سراغِ او؛ و «مقدمه» شد فرزندِ دوم، پس با آیتم‌های زوج رنگ گرفت. حتی اگر بنویسی .lesson:nth-child(2)، معنایش «دومین .lesson» نیست — یعنی «عنصری که هم دومین فرزند است و هم class اش lesson است». راهِ درست: فهرست را یک‌دست نگه‌دار. آن آیتمِ «نکته» جزوِ درس‌ها نیست، پس جایش داخلِ <ul> نیست؛ آن خط را پاک کن و نکته را در یک <p> بالای فهرست بگذار. حالا شمارش دوباره همان می‌شود که انتظار داری.

۳. :not(): همه، جز استثنا#

الان بین آیتم‌ها هیچ خطی نیست. می‌خواهی زیرِ هر درس یک خطِ جداکننده باشد — ولی نه زیرِ آخری، چون خودِ کادرِ فهرست پایین را می‌بندد. یک راهِ تمیز :not() است: داخلِ پرانتزش یک selector می‌گذاری و می‌گویی «همه، جز این‌ها».

.lessons > li:not(:last-child) {
  border-block-end: 1px solid #d8ccae;
}

چک کن: حالا زیرِ هر درس یک خطِ نازک افتاد، جز زیرِ «پروژهٔ پایانی». :not(:last-child) یعنی «هر li که آخرین نیست». :not() هر selector ای را می‌پذیرد؛ می‌توانی :not(.done) یا :not(:first-child) هم بنویسی.

۴. combinator ها: انتخاب بر اساسِ رابطه#

تا اینجا فاصلهٔ بینِ دو selector را با یک > نوشتیم بی‌اینکه اسمش را بگوییم. علامتی که رابطهٔ بینِ دو عنصر را می‌سازد combinator نام دارد، و سه تای پرکاربرد داریم:

  • > (فرزندِ مستقیم): .lessons > li یعنی فقط li هایی که مستقیماً فرزندِ .lessons اند. برای همین «حلقهٔ while» و «حلقهٔ for» که یک پله تودرترند راه‌راه و خط نگرفتند — آن‌ها فرزندِ مستقیم نیستند. اگر > را برداری و فقط .lessons li بنویسی (فاصله، یعنی descendant از ترم ۲)، آن دوتای تودرتو هم استایل می‌گیرند. همین را در آزمایشگاه امتحان کن و فرق را ببین.
  • + (خواهر-برادرِ چسبیده): li + li یعنی «هر li که بلافاصله بعد از یک li دیگر می‌آید» — یعنی همه جز اولی. .lessons > li:not(:last-child) را با .lessons > li + li عوض کن؛ می‌بینی تقریباً همان جداکننده‌ها می‌آید، این‌بار از زاویهٔ «بعدی».
  • ~ (خواهر-برادرِ بعدی): li:first-child ~ li یعنی «همهٔ li هایی که بعد از اولی می‌آیند»، نه فقط چسبیده. امتحانش کن و ببین همهٔ درس‌ها جز اولی رنگ می‌گیرند.

💡 نکته: فرقِ + و ~ را با یک جمله نگه‌دار: + فقط همان یکیِ بعدی را می‌گیرد، ~ همهٔ بعدی‌ها را. هر دو فقط رو به جلو نگاه می‌کنند — عنصرهای قبلی را نمی‌شود این‌طور انتخاب کرد.

۵. :is() و :where(): گروه‌کردنِ تمیز#

گاهی یک استایل را برای چند selector می‌خواهی. تا حالا با , گروه می‌کردی (li:first-child, li:last-child). :is() همان کار را کوتاه‌تر می‌کند: .lessons > li:is(:first-child, :last-child) یعنی «li ای که یا اولی است یا آخری». امتحانش کن:

.lessons > li:is(:first-child, :last-child) {
  letter-spacing: 0.5px;
}

چک کن: فقط اولی و آخری کمی بازتر شدند. :is() وقتی درخشان می‌شود که selector ها بلندند: به‌جای نوشتنِ دو بارِ .lessons > li ...، یک بار می‌نویسی و استثناها را داخلِ :is() می‌گذاری.

:where() دقیقاً مثلِ :is() است، با یک فرقِ مهم: specificity اش صفر است. یعنی هر قاعده‌ای که بعد بیاید خیلی راحت رویش را می‌گیرد. وقتی می‌خواهی یک استایلِ پایه بدهی که بقیه بتوانند بی‌دردسر عوضش کنند، :where() را انتخاب کن؛ وقتی می‌خواهی وزنِ عادیِ selector حفظ شود، :is(). (specificity را از ترم ۲ یادت هست.)

کد کاملِ این فصل#

اول index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>selector های ساختاری</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main class="page">
      <h1>فهرست درس‌ها</h1>
      <ul class="lessons">
        <li>مقدمه</li>
        <li>متغیرها</li>
        <li>شرط‌ها</li>
        <li>حلقه‌ها
          <ul>
            <li>حلقهٔ while</li>
            <li>حلقهٔ for</li>
          </ul>
        </li>
        <li>توابع</li>
        <li>پروژهٔ پایانی</li>
      </ul>
    </main>
  </body>
</html>

و styles.css:

/* styles.css */
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: Tahoma, sans-serif;
  background-color: #f6edda;
  color: #2c2114;
  line-height: 1.8;
}

.page {
  padding: 24px;
}

.lessons {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 420px;
  border: 1px solid #d8ccae;
  border-radius: 10px;
  overflow: hidden;
}

/* faghat li-haye mostaghim, na li-haye tou-dar-tou */
.lessons > li {
  padding: 12px 16px;
}

/* zebra: li-haye zoj rang-e paszamine-ye motefavet */
.lessons > li:nth-child(even) {
  background-color: #f0e7d2;
}

/* avvalin dars: bardaste va abi */
.lessons > li:first-child {
  font-weight: 700;
  color: #2a6b93;
}

/* akharin dars: rang-e taakid */
.lessons > li:last-child {
  color: #bc3e1c;
}

/* khat-e jodakonande zir-e hame joz akhari */
.lessons > li:not(:last-child) {
  border-block-end: 1px solid #d8ccae;
}

/* zir-list-e tou-dar-too: kami tou-rafte va sabz */
.lessons ul {
  list-style: none;
  margin-block: 8px 0;
  margin-inline: 16px 0;
  padding: 0;
  color: #55731b;
}

یک نمودارِ ساده از رابطهٔ عنصرها: یک جعبهٔ والد با چند جعبهٔ فرزند؛ یک فلش فقط به فرزندانِ مستقیم، یک فلش از یک جعبه به همان جعبهٔ بلافاصله‌بعد، و یک فلش از یک جعبه به همهٔ جعبه‌های بعد از آن

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

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

کلمه تلفظ به حروف فارسی یعنی چه
:nth-child() ان‌ث-چایلد فرزندی را با یک الگوی جایگاهی (even/odd/عدد) انتخاب می‌کند
:first-child فرست-چایلد اولین فرزندِ والد
:last-child لست-چایلد آخرین فرزندِ والد
:not() نات همه، جز آنچه داخلِ پرانتز است
combinator کامباینِیتور علامتی که رابطهٔ بینِ دو selector را می‌سازد
> فرزندِ مستقیم فقط فرزندانِ بی‌واسطه
+ خواهر-برادرِ چسبیده فقط عنصرِ بلافاصله‌بعد
~ خواهر-برادرِ بعدی همهٔ عنصرهای بعد
:is() / :where() ایز / وِر چند selector را گروه می‌کنند؛ :where() با specificity صفر

تمرین‌ها

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

در فصل بعد#

الان می‌توانی عنصرها را بر اساسِ حالت و جایگاه و رابطه انتخاب کنی. فصل بعد یک قدم عجیب‌تر برمی‌داریم: با ::before و ::after می‌توانی محتوایی روی صفحه بسازی که اصلاً در HTML نیست — علامتِ نقل‌قول، نشانِ کنارِ عنوان، یک فلشِ کوچک — همه فقط با CSS و یک property تازه به اسمِ content.

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

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