IlmHamroh
JavaScript Full-stack/5-qism. CSS asoslari va layout6/63-dars17 daqiqa
Mundarija (36)

CSS :has, :is, :where va :not: mantiqiy pseudo-klasslar

Qisqacha: Mantiqiy pseudo-klasslar qavs ichiga selektorlar ro'yxatini oladi. Masalan, :is(header, footer) a uzun ro'yxatni qisqartiradi. Uning egizagi :where() xuddi shunday ishlaydi, faqat og'irligi nol. Inkor :not(.achchiq) mos kelmaganlarini tanlaydi. Eng kuchlisi :has() "ichida X bor" yoki "keyinida X turgan" elementni tanlaydi: article:has(img) — rasmli karta, label:has(~ :user-invalid) — xatoli maydonning yorlig'i. :is() va :where() ichidagi noto'g'ri selektor butun qoidani buzmaydi, :not() va :has() ichidagisi esa buzadi.

Bu darsda

  • :is() bilan takroriy selektorlarni qisqartirasiz va "kechirimli ro'yxat"ni tushuntirasiz.
  • :where() ning :is() dan farqini bilasiz.
  • :not() bilan inkor yozasiz va uning mashhur tuzog'idan qochasiz.
  • :has() bilan ota va oldingi qo'shnini tanlaysiz: rasmli karta, xatoli maydon, tanlangan variant.
  • :has() qoidalari va tezlik bo'yicha tavsiyalarni bilasiz.

Oldin bilishingiz kerak: Holat pseudo-klasslari, Tuzilma pseudo-klasslari, Asosiy selektorlar va kombinatorlar.

1. Nega bu kerak?

Oldingi darslardan uchta ochiq savol qoldi:

  1. Uzun ro'yxatlar. header a:hover, nav a:hover, footer a:hover — bir xil qism uch marta. Yana Asosiy selektorlar darsidagi xavf: ro'yxatdagi bitta noto'g'ri selektor butun qoidani o'chiradi.
  2. "Boshqa hammasi". Joriy sahifadan tashqari hamma havolaga hover kerak. Hozircha "shundan tashqari" deyishning yo'li yo'q.
  3. Orqaga qarash. Kombinatorlar faqat pastga (ota → bola) va oldinga (oldingi → keyingi) qaraydi. Holat pseudo-klasslari darsida label maydondan oldin turgani uchun uni xato maydonga qarab bo'yay olmadik.

«Bahor»da Jasur aka ofitsiant Otabekka shunday topshiriqlar beradi:

  • "Zal yoki ayvondagi stollarga non qo'y" — ro'yxatdan istalgani.
  • "7-stoldan boshqa hamma stolga choy qo'y" — inkor.
  • "Bolali oila o'tirgan stolga bolalar stulini olib bor" — stolni ichidagi odamlarga qarab tanlash.

CSS'da bu uch topshiriqqa to'rtta vosita javob beradi: :is(), :where(), :not() va :has(). Ular mantiqiy pseudo-klasslar (logical pseudo-classes) deb ataladi: ichiga selektorlarni olib, ular ustida "yoki", "emas", "ichida bor" amallarini bajaradi.

2. :is() — ro'yxatdan istalgani

2.1 Takrorni qisqartirish

:is() qavs ichidagi selektorlardan istalganiga mos elementni tanlaydi:

css
/* Uzun yo'l */
header a:hover,
nav a:hover,
footer a:hover {
  color: #b4461a;
}

/* Xuddi shu — :is() bilan */
:is(header, nav, footer) a:hover {
  color: #b4461a;
}

O'qilishi: "header yoki nav yoki footer ichidagi, ustida sichqoncha turgan havola". Umumiy qism bir marta yoziladi, farq qiladigan qism — qavs ichida.

:is() selektorning istalgan joyida turadi. Ikkitasi birga bo'lsa, brauzer hamma juftlarni o'zi tuzadi:

html
<style>
  main :is(h2, h3) :is(a, strong) {
    color: #b4461a;
  }
</style>
<main>
  <h2>Bugun <strong>chegirma</strong></h2>
  <h3><a href="menyu/">Menyuni ko'rish</a></h3>
  <p>Oddiy <strong>paragraf</strong> — bunga tegmaydi.</p>
</main>

Nimaga qarang: sarlavhalardagi strong va havola qizg'ish, paragrafdagi strong esa qora. Bitta qator to'rtta selektorni almashtirdi: main h2 a, main h2 strong, main h3 a, main h3 strong.

2.2 Kechirimli ro'yxat

Asosiy selektorlar darsida va'da bergan edik: vergulli ro'yxatdagi bitta xato butun qoidani o'chiradi, :is() esa bundan qutqaradi. Solishtiramiz:

html
<style>
  /* ❌ #1-taom noto'g'ri — butun qoida tashlanadi */
  h2, #1-taom { color: #b4461a; }
  /* ✅ :is() ichida noto'g'ri qismni tashlab, qolganini oladi */
  :is(h3, #1-taom) { color: #2f6b3a; }
</style>
<h2>Issiq taomlar</h2>
<h3>Osh</h3>

Nimaga qarang: h2 qora qoldi, h3 esa yashil. Chrome'da tekshirdik: DevTools ikkinchi qoidani :is(h3) deb ko'rsatdi — noto'g'ri qism jimgina olib tashlandi. Birinchi qoida esa umuman yo'q.

Bunday ro'yxat kechirimli (forgiving selector list) deb ataladi. Bu qachon kerak? Ro'yxatga yangi yoki faqat ba'zi brauzerlarda bor selektor qo'shganda. Eski brauzer uni tushunmasa, qolgan qism baribir ishlaydi.

2.3 Og'irligi va cheklovi

Og'irlik haqida bitta gap: :is() o'z ichidagi eng og'ir selektorning og'irligini oladi. Masalan, :is(p, #asosiy) oddiy p ni tanlaganda ham id kabi og'ir bo'ladi. Bu qanday hisoblanishini Specificity darsida ko'ramiz.

Cheklovi: :is() ichida pseudo-element (keyingi darsdagi ::before kabi) ishlamaydi — brauzer uni kechirimli ro'yxatdan jimgina chiqarib tashlaydi. Chrome'da tekshirdik.

3. :where() — og'irligi nol egizak

:where() :is() bilan bir xil ishlaydi: o'sha "yoki", o'sha kechirimli ro'yxat. Farqi — bitta: :where() ning og'irligi nol. Unda qanday selektor yozilmasin, u to'qnashuvda hech narsa qo'shmaydi.

Nega bu kerak? Ba'zi qoidalar "boshlang'ich" bo'ladi: hamma joyga umumiy ko'rinish beradi, keyin esa aniq joylarda o'zgartiriladi. :where() li qoidani har qanday oddiy qoida bosib keta oladi.

css
/* Boshlang'ich qoida: hamma sarlavhalar yashil */
:where(h1, h2, h3) {
  color: #2f6b3a;
}

Bunday qoidalarni CSS reset va katta kutubxonalarda ko'p uchratasiz. Qisqasi: :is() — eng og'ir argumentcha, :where() — nol; hisobi Specificity darsida.

Tekshirib ko'ring: :where(h2, #1-taom) { color: #2f6b3a; } — bu qoida h2 ni yashil qiladimi?

Javob

Ha. :where() ham :is() kabi kechirimli: noto'g'ri #1-taom tashlanadi, h2 qoladi. Chrome'da tekshirdik. Oddiy vergulli ro'yxat bo'lganda edi, h2 ham bo'yalmasdi.

4. :not() — "shundan boshqa"

4.1 Oddiy inkor

:not() qavs ichidagi selektorga mos kelmaydigan elementni tanlaydi:

  • li:not(:last-child) — oxirgisidan boshqa hamma qator. Qatorlar orasidagi chiziq uchun — Tuzilma pseudo-klasslari darsidagi li:last-child { border-bottom: 0 } ning bitta qatorli varianti.
  • a:not([href^="https://"]) — boshqa saytga olib bormaydigan havolalar.
  • input:not([type="checkbox"], [type="radio"]) — qavs ichida ro'yxat ham bo'ladi: "na checkbox, na radio" — ya'ni matn yoziladigan maydonlar.

Jasur akaning ikkinchi topshirig'i — joriy sahifadan boshqa havolalarga hover:

html
<style>
  nav a {
    color: #2f6b3a;
    padding: 2px 6px;
  }
  nav a:not([aria-current="page"]):hover {
    background-color: #fffaf0;
    color: #b4461a;
  }
  nav [aria-current="page"] {
    color: #1f2a24;
    font-weight: bold;
  }
</style>
<nav aria-label="Asosiy menyu">
  <a href="../">Bosh sahifa</a>
  <a href="./" aria-current="page">Menyu</a>
  <a href="../bron/">Stol band qilish</a>
</nav>

Nimaga qarang: "Bosh sahifa" va "Stol band qilish" ustida fon va rang o'zgaradi. "Menyu" esa jim turadi — mehmon allaqachon shu sahifada, uni "bosing" deb chaqirishning ma'nosi yo'q. :not() boshqa pseudo-klasslar bilan bir qatorda yopishib turadi: a:not(...):hover.

4.2 Murakkab inkor va mashhur tuzoq

Qavs ichiga kombinatorli selektor ham yozish mumkin. a:not(nav a) — "nav ichida bo'lmagan havola". Chrome'da tekshirdik: logotip havolasi tanlandi, menyu havolasi esa yo'q.

Endi uning "o'xshashi"ni ko'ring: :not(nav) a. Ko'pchilik uni ham "nav ichida bo'lmagan havola" deb o'qiydi. Aslida u "nav bo'lmagan qandaydir ota ichidagi havola" degani. Menyu havolasining otalari orasida header, body, html bor — ular nav emas. Demak, shart bajarildi. Chrome'da tekshirdik: :not(nav) a menyu havolasini ham, logotipni ham tanladi.

Qoida: "X ichida bo'lmagan Y" — Y:not(X Y). Inkor butun zanjirni o'rab olsin.

4.3 Kechirimli emas

:not() :is() dan farqli — kechirimsiz. Chrome'da tekshirdik: :not(h2, #1-taom) butun qoidani tashlab yubordi. Ichidagi har bir selektor to'g'ri bo'lishi shart.

Yana bir tuzoq: :not() yolg'iz yozilsa, juda keng ushlaydi. :not(p) — sahifadagi paragrafdan boshqa hamma narsa, html, head va body ham. Uni doim biror selektorga yopishtiring: main > :not(h1).

Tekshirib ko'ring: footer dagi havolalardan tashqari hamma havolalarni qalin qiladigan selektor yozing.

Javob

a:not(footer a). O'qilishi: "havola, lekin footer ichidagi havola emas". :not(footer) a esa noto'g'ri — footer havolasining ham footer bo'lmagan otasi bor (body), shuning uchun u ham tanlanib qolardi.

5. :has() — "ichida X bor"

5.1 Yangi yo'nalish

Hozirgacha hamma selektorlar bir tomonga qaragan: ota → bola, oldingi → keyingi. :has() birinchi marta teskari yo'lni ochdi:

flowchart TD
  D["div"] --> L["label"]
  D --> S["span — yo'riqnoma"]
  D --> I["input — :user-invalid"]
  Y["label:has(~ :user-invalid)"] -.->|"tanlaydi"| L
  X["div:has(> :user-invalid)"] -.->|"tanlaydi"| D
  L ~~~ S ~~~ I

Nimaga qarang: xato holati input da, lekin tanlanadigan elementlar — uning otasi div va undan oldin turgan label. Kombinatorlar bilan bunday qilib bo'lmasdi: ular faqat pastga va oldinga qaraydi. Shuning uchun dasturchilar :has() ni "ota selektori" (parent selector) deb ham atashadi.

:has() qavsi ichida selektor "shu elementdan boshlab" o'qiladi:

  • article:has(img) — ichida istalgan chuqurlikda img bor article;
  • article:has(> img) — img bevosita bolasi bo'lgan article;
  • h2:has(+ p) — darhol keyinida paragraf turgan h2;
  • label:has(~ input) — keyingi qo'shnilari orasida input bor label.

Bu qavs ichidagi yozuv nisbiy selektor (relative selector) deb ataladi: u kombinator bilan boshlanishi mumkin, chunki uning boshlang'ich nuqtasi — :has() yopishgan element.

5.2 Rasmli karta

Asosiy selektorlar darsida va'da qilingan "ichida rasm bor karta":

html
<style>
  .taom-karta {
    border: 1px solid #5b6660;
    padding: 0 12px;
    margin: 8px 0;
  }
  .taom-karta:has(figure) {
    border: 2px solid #2f6b3a;
    background-color: #fffaf0;
  }
</style>
<article class="taom-karta">
  <figure>
    <svg role="img" aria-label="Osh laganda"
         width="80" height="50" viewBox="0 0 80 50">
      <ellipse cx="40" cy="25" rx="38" ry="22" fill="#e8c07d"/>
    </svg>
    <figcaption>Choyxona oshi</figcaption>
  </figure>
  <h3>Osh — 35 000 so'm</h3>
</article>
<article class="taom-karta">
  <h3>Lag'mon — 28 000 so'm</h3>
</article>

Nimaga qarang: rasmli osh kartasi yashil qalin ramkada va qaymoq fonda, rasmsiz lag'mon kartasi — oddiy. HTML'da class qo'shilmadi: Rustam aka kartaga rasm qo'shsa, u o'zi "rasmli karta" bo'ladi. Rasm o'rnida biz inline SVG chizdik — figure ichida istalgan rasm bo'lishi mumkin (figure va figcaption).

Teskarisi ham oson: .taom-karta:not(:has(figure)) — rasmsiz kartalar. Mantiqiy pseudo-klasslar bir-birining ichida ishlaydi.

5.3 Forma: xato maydonning yorlig'i

Holat pseudo-klasslari darsidagi muammo: label maydondan oldin turadi, + esa orqaga qaramaydi. :has() bilan bu hal bo'ladi:

html
<style>
  label:has(~ :user-invalid) {
    color: #b4461a;
    font-weight: bold;
  }
  input:user-invalid {
    border: 2px solid #b4461a;
  }
  fieldset:has(:user-invalid) legend {
    color: #b4461a;
  }
</style>
<form action="https://example.com/bron" method="post">
  <fieldset>
    <legend>Siz haqingizda</legend>
    <div>
      <label for="ism">Ismingiz</label>
      <input id="ism" name="ism" type="text" required>
    </div>
    <div>
      <label for="telefon">Telefon raqamingiz</label>
      <span id="telefon-yordam">Masalan: +998 90 000 00 00.</span>
      <input id="telefon" name="telefon" type="tel" required
             pattern="[0-9 +\-]{9,17}"
             aria-describedby="telefon-yordam">
    </div>
  </fieldset>
  <button type="submit">Stolni band qilish</button>
</form>

Nimaga qarang: ismni yozib o'chiring va Tab bosing. Maydon ramkasi bilan birga uning yorlig'i ham qizg'ish va qalin bo'ladi. Guruh sarlavhasi "Siz haqingizda" ham qizaradi — mehmon qaysi guruhda xato borligini uzoqdan ko'radi. Ismni to'ldirsangiz, uchalasi ham o'z holiga qaytadi.

Telefon maydonida label va input orasida yo'riqnoma span turibdi. Shuning uchun + emas, ~ ishlatdik: "keyingi qo'shnilari orasida xato maydon bor label".

Holat pseudo-klasslari darsida form:invalid ni ko'rgan edik — u faqat "formada qayerdadir xato bor" deydi. :has() esa aynan qaysi guruh va qaysi yorliq ekanini ko'rsatadi.

5.4 Tanlangan variant va miqdor so'rovi

Radio tugmali qatorni butunligicha belgilash — ota div ni ichidagi :checked ga qarab tanlaymiz:

css
div:has(> :checked) {
  background-color: #fffaf0;
}

O'qilishi: "bevosita bolasi tanlangan tugma bo'lgan div". Holat pseudo-klasslari darsidagi "Qayerda o'tirasiz?" guruhini eslang: har radio tugma yorlig'i bilan birga alohida div da turardi. Endi "Ayvon" tanlansa, uning butun qatori — tugma ham, yorliq ham — qaymoq fonga kiradi. Buni pastdagi mashqlardan birida o'zingiz sinaysiz.

Tuzilma pseudo-klasslari darsidagi miqdor so'rovi ham ancha qisqaradi. "Kamida 5 qatorli ro'yxat":

css
ul:has(> li:nth-child(5)) {
  font-size: 14px;
}

O'qilishi: "beshinchi bolasi bor ul". Beshinchi qator bo'lsa — demak, qatorlar kamida beshta. Chrome'da tekshirdik: uch qatorli ro'yxat o'zgarmadi, besh qatorlisi kichraydi. Endi o'zgarish qatorlarga emas, ro'yxatning o'ziga beriladi.

5.5 Hammasi birga: xiralashadigan menyu

Mantiqiy pseudo-klasslar bir-birining ichida va holat pseudo-klasslari bilan birga ishlaydi. Mashhur usul: sichqoncha menyudagi bitta havola ustida tursa, qolganlari xiralashadi va ko'z tanlanganiga tushadi:

html
<style>
  nav a {
    color: #2f6b3a;
  }
  nav:has(a:hover) a:not(:hover) {
    color: #5b6660;
  }
</style>
<nav aria-label="Asosiy menyu">
  <a href="menyu/">Menyu</a> ·
  <a href="bron/">Stol band qilish</a> ·
  <a href="aloqa/">Aloqa</a>
</nav>

Nimaga qarang: havolalardan biriga sichqonchani olib boring — qolgan ikkitasi kulrang bo'ladi, tanlangan esa yashilligicha qoladi. Selektorni o'ngdan chapga o'qing: "sichqoncha ustida bo'lmagan havola, lekin faqat ichida hover qilingan havola bor nav da". Uch darsdagi uchta vosita — :hover, :not(), :has() — bitta qatorda birlashdi.

Tekshirib ko'ring: h2:has(+ p) va h2 + p farqi nima?

Javob

Ikkalasi bir xil juftlikni topadi, lekin boshqa elementni bo'yaydi. h2 + p — paragrafni tanlaydi (sarlavhadan keyingisini). h2:has(+ p) — sarlavhani tanlaydi (ostida paragraf turganini). Selektorning eng o'ng qismi hali ham "kimni bo'yaymiz"ni bildiradi, :has() esa unga shart qo'shadi.

6. :has() qoidalari va tezlik

6.1 Uchta cheklov

Chrome'da uchalasini tekshirdik — har birida brauzer butun qoidani tashladi:

  • Ichma-ich bo'lmaydi. :has(:has(img)) — noto'g'ri.
  • Pseudo-element bo'lmaydi. :has(::before) — noto'g'ri.
  • Kechirimli emas. article:has(img, #1-taom) — bitta xato qism butun qoidani o'chirdi.
css
/* ❌ uchalasini ham brauzer tashlaydi */
article:has(:has(img)) { color: #b4461a; }
article:has(::before) { color: #b4461a; }
article:has(figure, #1-taom) { color: #b4461a; }

6.2 Brauzerlarda va zaxira

:has() 2023-yil dekabridan hamma asosiy brauzerlarda ishlaydi. 2026-yil iyunidan esa Baseline "widely available" (Veb standartlari). Bugun uni bemalol ishlatsa bo'ladi. Lekin eski telefonni unutmang: :has() ni tanimaydigan brauzer butun qoidani tashlab ketadi — xuddi noto'g'ri selektor kabi.

Shuning uchun ikki qoida:

  1. :has() li selektorni oddiy selektorlar bilan bitta vergulli ro'yxatga qo'shmang. Aks holda eski brauzerda oddiylari ham o'chib ketadi.
  2. :has() ni qo'shimcha bezak uchun ishlating. Asosiy ma'lumot — xato matni, narx — usiz ham ko'rinsin (Brauzer mosligi va progressive enhancement).

Brauzer biror selektorni biladimi-yo'qmi — CSS'ning o'zida tekshirish usuli ham bor. Uni Brauzer mosligi va @supports darsida o'rganamiz.

6.3 Tezlik

:has() brauzerdan ko'proq ish talab qiladi. Oddiy selektorda brauzer elementdan yuqoriga qarab tekshiradi. :has() da esa har o'zgarishda "ichida X paydo bo'ldimi?" deb qayta so'rashi kerak. MDN ikki maslahat beradi:

  • :has() ni body, :root yoki * kabi juda umumiy elementga yopishtirmang. body:has(...) sahifadagi har qanday o'zgarishda butun hujjatni qayta tekshirtiradi. Aniq konteyner — kerakli elementlarni o'rab turgan eng yaqin ota — tanlang: .taom-karta:has(...), form:has(...).
  • Qavs ichida iloji boricha > yoki + ishlating: article:has(> figure). Shunda brauzer butun ichki daraxtni emas, faqat bolalarni yoki qo'shnini ko'radi.

Oddiy sahifada farqni sezmaysiz. Minglab elementli katta ilovalarda esa bu maslahatlar muhim.

7. Ko'p uchraydigan xatolar

7.1 :not(X) Y — "X ichida bo'lmagan Y" emas

:not(nav) a hamma havolani tanlaydi. Tuzatish: a:not(nav a).

7.2 :not() yoki :has() ichida xato

Bitta noto'g'ri qism — butun qoida tashlanadi. Tuzatish: ichidagi har selektorni alohida tekshiring (DevTools'dagi Ctrl + F bilan — Asosiy selektorlar).

7.3 :has() li selektor vergulli ro'yxatda

h2, .taom-karta:has(figure) { ... } — eski brauzerda h2 ham bo'yalmaydi. Tuzatish: :has() li selektorga alohida qoida yozing.

7.4 body:has() hamma narsa uchun

Ishlaydi, lekin sekinroq. Tuzatish: eng yaqin aniq konteynerga yopishtiring.

7.5 :is() ichida pseudo-element

:is(p, ::before) — ::before qismi jimgina tashlanadi. Tuzatish: pseudo-elementni :is() dan tashqarida yozing: :is(h2, h3)::before.

7.6 Yolg'iz :not()

:not(p) { ... } — html va body ham tushadi. Tuzatish: :not() ni aniq selektorga yopishtiring.

8. Mashqlar

1-mashq (oson): Qisqartiring

Quyidagi qoidalarni :is() va :not() bilan qisqaroq yozing:

css
header a:focus-visible,
nav a:focus-visible,
footer a:focus-visible {
  outline: 3px solid #1a5d1a;
}

main h2 strong,
main h3 strong {
  color: #b4461a;
}

Va yana bitta: oxirgisidan boshqa hamma li ga 1px solid #5b6660 pastki chiziq.

Yechim
css
:is(header, nav, footer) a:focus-visible {
  outline: 3px solid #1a5d1a;
}

main :is(h2, h3) strong {
  color: #b4461a;
}

li:not(:last-child) {
  border-bottom: 1px solid #5b6660;
}

Umumiy qismlar bir marta yozildi, farq qiladigani qavsga olindi. Uchinchi qoida o'tgan darsdagi ikki qoidani (li ga chiziq, li:last-child dan olib tashlash) bittaga birlashtirdi.

2-mashq (o'rta): Bron formasining "Qayerda o'tirasiz?" guruhi

«Bahor» formasidagi joy guruhi:

html
<fieldset>
  <legend>Qayerda o'tirasiz?</legend>
  <div>
    <input id="joy-zal" name="joy" type="radio" value="zal" checked>
    <label for="joy-zal">Umumiy zal</label>
  </div>
  <div>
    <input id="joy-xona" name="joy" type="radio" value="xona"
           aria-describedby="xona-yordam">
    <label for="joy-xona">Alohida xona</label>
    <span id="xona-yordam">8 kishidan boshlab.</span>
  </div>
</fieldset>

CSS yozing:

  1. Tanlangan variantning butun qatori — #fffaf0 fonda.
  2. "Alohida xona" tanlanganda uning yo'riqnomasi — qizg'ish (#b4461a) va qalin: mehmon shartni ko'rsin.
  3. Tanlanmagan variantlarning yorlig'i — kulrang (#5b6660).

Ishora: 1-band — div:has(> :checked). 2-band — yo'riqnoma tugmadan keyin turibdi, ~ yetadi. Yo'riqnomani id'si -yordam bilan tugashi bo'yicha toping (Atribut selektorlari). 3-band — :not() va :checked: yorliq tugmadan keyin turadi (+).

Yechim
html
<style>
  div:has(> :checked) {
    background-color: #fffaf0;
  }
  input:checked ~ span[id$="-yordam"] {
    color: #b4461a;
    font-weight: bold;
  }
  input:not(:checked) + label {
    color: #5b6660;
  }
</style>
<fieldset>
  <legend>Qayerda o'tirasiz?</legend>
  <div>
    <input id="joy-zal" name="joy" type="radio" value="zal"
           checked>
    <label for="joy-zal">Umumiy zal</label>
  </div>
  <div>
    <input id="joy-xona" name="joy" type="radio" value="xona"
           aria-describedby="xona-yordam">
    <label for="joy-xona">Alohida xona</label>
    <span id="xona-yordam">8 kishidan boshlab.</span>
  </div>
</fieldset>
  • "Alohida xona" ni bosing: fon ikkinchi qatorga ko'chadi, yo'riqnoma qizaradi, "Umumiy zal" yorlig'i esa kulrang bo'ladi.
  • 2-band uchun :has() kerak emas: tugma yo'riqnomadan oldin turibdi, oddiy ~ yetadi. :has() ni faqat haqiqatan orqaga yoki yuqoriga qarash kerak bo'lganda ishlating — u qimmatroq.
  • 2-band selektori faqat "xona" uchun yozilmadi: ertaga "Ayvon" ga ham yo'riqnoma qo'shilsa, u ham tanlanganda qizaradi.

3-mashq (qiyin): Stajyorning selektorlari

Stajyor menyu sahifasiga shunday CSS yozdi:

css
:not(footer) a { font-weight: bold; }
body:has(.achchiq) h1 { color: #b4461a; }
h2, .taom-karta:has(> figure) { border-bottom: 2px solid #2f6b3a; }
.taom-karta:has(figure, #1-rasm) { background-color: #fffaf0; }

Jasur aka niyati: footer'dan boshqa havolalar qalin; menyuda achchiq taom bo'lsa, sarlavha qizg'ish; sarlavhalar va rasmli kartalar tagida yashil chiziq; rasmli kartalar — qaymoq fonda.

Har qatorning muammosini toping va tuzating.

Ishora: "Murakkab inkor va mashhur tuzoq" hamda ":has() qoidalari va tezlik" bo'limlarini qayta o'qing. Bitta qatorning muammosi zamonaviy brauzerda umuman ko'rinmaydi.

Yechim
css
a:not(footer a) { font-weight: bold; }
main:has(.achchiq) h1 { color: #b4461a; }
h2 { border-bottom: 2px solid #2f6b3a; }
.taom-karta:has(> figure) { border-bottom: 2px solid #2f6b3a; }
.taom-karta:has(figure) { background-color: #fffaf0; }
  1. :not(footer) a — footer havolalari ham tanlanadi: ularning body otasi footer emas. To'g'risi — a:not(footer a).
  2. body:has(.achchiq) — ishlaydi, lekin sahifadagi har o'zgarishda butun hujjat qayta tekshiriladi. main — yaqinroq va aniqroq konteyner.
  3. h2, .taom-karta:has(> figure) — zamonaviy Chrome'da ishlaydi. Lekin :has() ni bilmaydigan eski brauzer butun qoidani tashlaydi va h2 chizig'i ham yo'qoladi. Ikki qoidaga ajratdik — bu muammo ko'zga ko'rinmaydigan qator edi.
  4. :has(figure, #1-rasm) — #1-rasm noto'g'ri id selektori, :has() esa kechirimsiz. Butun qoida tashlandi. Keraksiz qismni olib tashladik.

4-mashq: Portfolio qadami — mantiqiy selektorlar

Portfolio HTML'iga tegmaymiz. asosiy.css ni yangilang:

  1. Birinchi darsdan beri turgan h1, h2 qoidasini :where() ga o'tkazing va unga h3 ni ham qo'shing — bu sarlavhalar uchun "boshlang'ich" rang.
  2. Holat pseudo-klasslari darsidagi a:hover foni joriy sahifa havolasiga tushmasin.
  3. Aloqa formasi: xato maydonning yorlig'i ham qizg'ish (#b4461a) bo'lsin. Portfolio formasida har maydon p ichida, label — maydondan oldin.
  4. "Qaysi mavzuda?" guruhida mavzu tanlanmay qolgan bo'lsa (yuborishga urinishdan keyin), guruh sarlavhasi — qizg'ish.

Ishora: 2-band — :not([aria-current="page"]) ni a va :hover orasiga qo'ying. 3-band — label:has(~ :user-invalid). 4-band — fieldset:has(:user-invalid) legend.

Yechim

asosiy.css da h1, h2 qoidasi va a:hover qoidasi almashtiriladi, qolgani oxiriga qo'shiladi:

css
/* Sarlavhalar uchun boshlang'ich rang (og'irligi nol) */
:where(h1, h2, h3) {
  color: #1f6f4a;
}

/* Hover — joriy sahifadan boshqa havolalarga */
a:not([aria-current="page"]):hover {
  background-color: #e8f1ec;
}

/* Aloqa formasi: xato maydonning yorlig'i */
label:has(~ :user-invalid) {
  color: #b4461a;
}

/* Tanlanmagan mavzu guruhi */
fieldset:has(:user-invalid) legend {
  color: #b4461a;
}

Aloqa formasining bir qismi:

html
<style>
  body {
    font-family: system-ui, sans-serif;
    color: #1f2a24;
    background-color: #f7faf8;
  }
  :where(h1, h2, h3) { color: #1f6f4a; }
  input:user-invalid,
  textarea:user-invalid {
    border: 2px solid #b4461a;
  }
  label:has(~ :user-invalid) { color: #b4461a; }
  fieldset:has(:user-invalid) legend { color: #b4461a; }
</style>
<h2>Xabar yuborish</h2>
<form action="https://example.com/aloqa" method="post">
  <p>
    <label for="ism">Ismingiz</label>
    <input id="ism" name="ism" type="text"
           autocomplete="name" required maxlength="60">
  </p>
  <fieldset>
    <legend>Qaysi mavzuda?</legend>
    <p>
      <input id="mavzu-ish" name="mavzu" type="radio"
             value="ish" required>
      <label for="mavzu-ish">Ish taklifi</label>
    </p>
    <p>
      <input id="mavzu-savol" name="mavzu" type="radio"
             value="savol">
      <label for="mavzu-savol">Savol</label>
    </p>
  </fieldset>
  <button type="submit">Yuborish</button>
</form>

Nozik joylar:

  • Hech narsa to'ldirmasdan "Yuborish" ni bosing: ism yorlig'i va ramkasi qizaradi, "Qaysi mavzuda?" sarlavhasi ham. Mavzuni tanlasangiz, guruh sarlavhasi darhol qaytadi.
  • :where() ga o'tganda ko'rinish o'zgarmadi — sarlavhalar hali ham yashil. Farq keyinroq chiqadi: biror joyda sarlavhaga boshqa rang kerak bo'lsa, oddiy h2 qoidasi ham uni bosib keta oladi.
  • Radio yorlig'i (Ish taklifi) tugmadan keyin turadi, shuning uchun label:has(~ ...) unga tushmaydi. Bu yaxshi: guruh sarlavhasi yetarli.
  • Joriy sahifa havolasi endi hoverda o'zgarmaydi, lekin klaviatura fokusi halqasi unda qoladi — Bobur uchun u baribir havola.

9. Real ishda

  • Formalar — :has() ning eng ko'p ishlatiladigan joyi: xatoli guruhni, tanlangan kartani, to'ldirilgan qadamni belgilash. Ilgari buning uchun JavaScript yozilardi.
  • Komponent kutubxonalari o'z boshlang'ich stillarini :where() bilan yozadi. Shunda siz ularni oddiy class bilan osongina o'zgartirasiz. Buni CSS reset darsida ko'rasiz.
  • Tailwind (kursda alohida o'rganamiz) has-[...]:, not-[...]: yozuvlari va group/peer usullari aynan shu pseudo-klasslar ustiga qurilgan (Tailwind variantlari).
  • Intervyuda so'raladi: "CSS'da ota selektori bormi?" (ha, :has()), ":is() va :where() farqi?", ":not(.a) .b va .b:not(.a .b) farqi?".

Xulosa

  • :is() — "ro'yxatdan istalgani": takrorni qisqartiradi va kechirimli — noto'g'ri qism butun qoidani buzmaydi.
  • :where() — :is() ning o'zi, faqat og'irligi nol; :is() esa eng og'ir argumentining og'irligini oladi.
  • :not() — inkor. "X ichida bo'lmagan Y" — Y:not(X Y), :not(X) Y emas. Kechirimsiz.
  • :has() — ota va oldingi qo'shnini tanlash: article:has(figure), label:has(~ :user-invalid), div:has(> :checked).
  • :has() ichma-ich bo'lmaydi, pseudo-element olmaydi, kechirimsiz; aniq konteynerga va >/+ bilan yozing.

Keyingi dars: Pseudo-elementlar — ::before va ::after bilan HTML'da yo'q bezaklarni chizamiz: achchiqlik belgisi, majburiy maydon yulduzchasi, ro'yxat markeri.

Manbalar

  • MDN: ":is()", ":where()", ":not()", ":has()" (tezlik bo'yicha tavsiyalar bilan) — developer.mozilla.org
  • W3C: "Selectors Level 4" — mantiqiy pseudo-klasslar va nisbiy selektorlar — w3.org/TR/selectors-4
  • web-features (Baseline ma'lumotlari): "has", "is", "where", "not" — github.com/web-platform-dx/web-features
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
CSS :has, :is, :where va :not: mantiqiy pseudo-klasslar — IlmHamroh