Kombinatorlar — descendant, child, sibling

~15 daq · CSS · CSS Asoslari

Muammodan boshlaylik

Saytda havolalar ko'k. Lekin menyudagi havolalar oq bo'lishi kerak, footer dagilar — kulrang. Har bir havolaga alohida class yozasizmi? 40 ta havola — 40 ta class="nav-link"?

Yoki: maqoladagi paragraflar orasida bo'shliq kerak, lekin birinchi paragrafdan oldin — yo'q.

Bu vazifalar elementlarning bir-biriga nisbatan joylashuvi bilan bog'liq: "menyu ichidagi havola", "paragrafdan keyingi paragraf". Buning uchun — kombinatorlar.


HTML — daraxt

Kombinatorlarni tushunish uchun HTML ni daraxt sifatida ko'ring:

<nav class="menu">
  <ul>
    <li><a href="/">Bosh sahifa</a></li>
    <li><a href="/kurslar">Kurslar</a></li>
  </ul>
  <a href="/kirish" class="login">Kirish</a>
</nav>
nav.menu                   ← ota (parent)
├── ul                     ← nav ning bolasi (child)
│   ├── li                 ← nav ning avlodi (descendant), ul ning bolasi
│   │   └── a              ← nav ning avlodi
│   └── li                 ← oldingi li ning qo'shnisi (sibling)
│       └── a
└── a.login                ← nav ning bolasi, ul ning qo'shnisi
  • Bola (child) — to'g'ridan-to'g'ri ichida, bir daraja
  • Avlod (descendant) — istalgan chuqurlikda ichida
  • Qo'shni (sibling) — bir xil ota ichida, yonma-yon

1. Descendant — ichidagi har qanday (bo'shliq)

.menu a {
  color: white;
}

.menu ichidagi barcha a — qanchalik chuqur bo'lmasin. Yuqoridagi misolda — uchala havola ham (ikkitasi li ichida, biri to'g'ridan-to'g'ri).

Muammo hal bo'ldi:

a { color: #2563eb; }
.menu a { color: white; }
.footer a { color: #94a3b8; }

Bitta class konteynerda — ichidagi hamma narsa boshqariladi.


2. Child — faqat to'g'ridan-to'g'ri bola (>)

.menu > a {
  font-weight: 700;
}

Faqat .menu ning bevosita bolasi bo'lgan a — ya'ni faqat "Kirish". li ichidagilar — yo'q.

Ichma-ich ro'yxatlarda juda muhim:

<ul class="tree">
  <li>Frontend
    <ul>
      <li>React</li>
      <li>Vue</li>
    </ul>
  </li>
  <li>Backend</li>
</ul>
.tree li { color: gray; }        /* barcha li — ichki ro'yxatdagilar ham */
.tree > li { font-weight: 700; } /* faqat yuqori daraja: Frontend, Backend */

3. Adjacent sibling — darhol keyingisi (+)

h2 + p {
  font-size: 18px;
  color: #475569;
}

h2 dan darhol keyin kelgan p — sarlavhadan keyingi "lid" paragraf. Faqat bitta — eng yaqini.

Darsning boshidagi 2-vazifa:

p + p {
  margin-top: 16px;
}

"Paragrafdan keyingi paragraf" — birinchisidan oldin bo'shliq yo'q, qolganlari orasida bor.

Formalarda:

input:invalid + .error-message {
  display: block;   /* noto'g'ri maydon ostidagi xabar ko'rinadi */
}

4. General sibling — keyingi barcha qo'shnilar (~)

h2 ~ p {
  margin-left: 16px;
}

h2 dan keyin kelgan barcha p qo'shnilar (bir xil ota ichida) — darhol keyingisi bo'lishi shart emas.

Mashhur "CSS-only" toggle:

<input type="checkbox" id="menu-toggle">
<label for="menu-toggle">☰ Menyu</label>
<nav class="mobile-menu">...</nav>
.mobile-menu { display: none; }

#menu-toggle:checked ~ .mobile-menu {
  display: block;
}

Checkbox belgilanganda — menyu ochiladi. JavaScript siz.

[!INFO] Sibling kombinatorlari faqat oldinga qaraydi: "keyingi qo'shni". "Oldingi qo'shni" ni tanlaydigan kombinator yo'q. Lekin :has() (3-dars) bilan mumkin: h2:has(+ p) — "darhol keyin p keladigan h2".


Barcha kombinatorlar

Kombinator Yozilishi Ma'nosi
Descendant A B (bo'shliq) A ichidagi har qanday B
Child A > B A ning bevosita bolasi B
Adjacent sibling A + B A dan darhol keyingi B
General sibling A ~ B A dan keyingi barcha B

Birlashtirish:

.card > .card-body p + p { ... }
/* .card ning bevosita bolasi .card-body ichidagi, boshqa p dan keyingi p */

Yaxshi amaliyot: selektorlarni qisqa saqlang

/* ❌ Juda uzun, mo'rt */
body div.container main section.products article.card div.body h3.title { ... }

/* ✅ Qisqa, aniq */
.card-title { ... }

Uzun selektorlar muammolari:

  • Mo'rt — HTML da bitta div qo'shilsa yoki o'chirilsa — stil ishlamay qoladi
  • Yuqori specificity — keyin o'zgartirish uchun yanada uzunroq selektor kerak (5-dars)
  • O'qish qiyin

Qoida: 1–3 daraja. Konteyner + element (.menu a) — yaxshi. Butun daraxt bo'ylab yo'l — yomon.


Nima uchun shunday?

Nima uchun kombinatorlar faqat "pastga" va "oldinga" ishlaydi — ota yoki oldingi qo'shnini tanlash nega bunchalik kech (:has() bilan) paydo bo'ldi?

Tezlik sababli. Brauzer HTML ni yuqoridan pastga o'qiydi va sahifa hali to'liq yuklanmasdan chizishni boshlaydi. "Pastga" va "oldinga" qaraydigan selektorlar uchun element paydo bo'lganda unga qaysi stil tegishli ekanini darhol bilish mumkin — kerakli ma'lumot (ota, oldingi qo'shnilar) allaqachon bor. "Ota ichida X bormi?" degan savol esa kelajakka bog'liq: X keyinroq yuklanishi mumkin, va har bir o'zgarishda butun daraxtni qayta tekshirish kerak bo'lardi. Brauzer muhandislari buni yillar davomida samarali qilish yo'lini izlashdi — :has() shu ishning natijasi.


Xulosa

Eslab qoling:

  • HTML — daraxt: ota, bola, avlod, qo'shni
  • A B — A ichidagi istalgan chuqurlikdagi B (eng ko'p ishlatiladi)
  • A > B — faqat bevosita bola
  • A + B — darhol keyingi qo'shni (p + p — paragraflar orasidagi bo'shliq)
  • A ~ B — keyingi barcha qo'shnilar (:checked ~ — CSS toggle)
  • Faqat pastga va oldinga; ota uchun — :has()
  • Selektorlarni qisqa saqlang: 1–3 daraja

Keyingi dars

5-dars: Specificity — qaysi stil ustunlik qiladi va nima uchun