Pseudo-class — :is(), :where(), :has(), :not()

~20 daq · CSS · Animatsiya va Ilg'or mavzular

Muammodan boshlaylik

3 va 5-darslarda bu to'rt pseudo-class bilan qisqacha tanishdik. Ular CSS ning so'nggi yillardagi eng kuchli qo'shimchalari: bir paytlar faqat JavaScript bilan qilinadigan ishlarni sof CSS ga o'tkazdi va selektorlarni ancha qisqartirdi.

Bu dars — ularning chuqur tahlili: specificity ga ta'siri, nozik jihatlari va real vazifalar.


:is() — selektorlar ro'yxatini qisqartirish

Takrorlanadigan qismli selektorlar:

/* ❌ uzun va takroriy */
.article h1 a,
.article h2 a,
.article h3 a,
.sidebar h1 a,
.sidebar h2 a,
.sidebar h3 a {
  color: inherit;
}

/* ✅ */
:is(.article, .sidebar) :is(h1, h2, h3) a {
  color: inherit;
}

:is(A, B, C) — "A, B yoki C dan istalgan biri". Kombinatsiyalarni brauzer o'zi hosil qiladi.

Kechirimli ro'yxat

2-darsdan: oddiy guruhlashda (a, b, c) bitta noto'g'ri selektor butun qoidani bekor qiladi. :is() ichida esa noto'g'ri yoki brauzer tushunmaydigan selektor shunchaki o'tkazib yuboriladi, qolganlari ishlaydi:

:is(input:focus-visible, input:yangi-pseudo-class) {
  outline: 2px solid #2563eb;   /* eski brauzer ham focus-visible qismini qo'llaydi */
}

Specificity — eng yuqorisi

:is() ning specificity si — ichidagi eng aniq selektorniki (5-dars):

:is(#header, .nav) a { }    /* (1, 0, 1) — #header tufayli, hatto .nav ga mos kelganda ham! */

[!WARNING] :is() ichiga ID qo'yish — butun qoidani ID darajasiga ko'taradi, element .nav orqali mos kelsa ham. Keyin uni oddiy class bilan o'zgartirib bo'lmaydi. :is() ichida bir xil "og'irlikdagi" selektorlar bo'lsin.


:where() — nol specificity

:where() — :is() ning egizagi, bitta farq bilan: specificity si har doim nol.

:where(.article, .sidebar) a { }    /* (0, 0, 1) — faqat "a" hisoblanadi */

Qachon kerak — oson o'zgartiriladigan standart stillar:

/* Reset va bazaviy stillar — har qanday class ularni yengsin */
:where(ul, ol)[role="list"] {
  list-style: none;
  padding: 0;
}

:where(button) {
  font: inherit;
  cursor: pointer;
}

Kutubxona va dizayn tizimlari mualliflari uchun ideal: kutubxona stillari :where() da bo'lsa, foydalanuvchi ularni bitta oddiy class bilan, !important va uzun selektorlarsiz o'zgartira oladi.

:is() :where()
Vazifa Bir xil — ro'yxatdan istalgani Bir xil
Specificity Ichidagi eng yuqorisi 0
Qachon Oddiy qisqartirish Bazaviy, reset, kutubxona stillari

:not() — istisno

.btn:not(.btn-primary) { background: #e2e8f0; }
li:not(:last-child) { border-bottom: 1px solid #e2e8f0; }
a:not([href^="/"]) { /* tashqi havolalar */ }

Zamonaviy :not() ro'yxat qabul qiladi:

input:not([type="checkbox"], [type="radio"]) {
  width: 100%;
  padding: 10px 12px;
}

"Checkbox va radio dan tashqari barcha input lar" — oldin ikkita :not() kerak edi.

Specificity — :is() kabi, ichidagi eng yuqorisi: .btn:not(.btn-primary) = (0, 2, 0).

"Oxirgisidan tashqari" naqshi

/* ❌ ikki qoida */
.menu li { border-right: 1px solid #ccc; }
.menu li:last-child { border-right: none; }

/* ✅ bitta */
.menu li:not(:last-child) { border-right: 1px solid #ccc; }

Yoki bugun — gap (14-dars) va chegaralar o'rniga divide naqshi; lekin ajratuvchi chiziqlar uchun :not(:last-child) — eng aniq.


:has() — ota selektor

CSS tarixidagi eng ko'p kutilgan imkoniyat. Selektorlar doim pastga qarardi (4-dars): "ota ichidagi bola". :has() — yuqoriga qaraydi: "ichida X bor bo'lgan element".

.card:has(img) {
  padding-top: 0;          /* rasmli kartochkalarda tepada padding yo'q */
}

Real vazifalar

Forma holati:

/* Ichida xato bo'lgan maydon guruhi */
.field:has(input:user-invalid) {
  --border: #dc2626;
}

.field:has(input:user-invalid) .error {
  display: block;
}

/* Majburiy maydonning label i */
label:has(+ input:required)::after {
  content: " *";
  color: #dc2626;
}

:user-invalid — :invalid dan farqli o'laroq, foydalanuvchi maydonni o'zgartirgandan keyingina ishlaydi: sahifa ochilganda bo'sh formaning hammasi qizil bo'lib turmaydi.

Checkbox bilan holat — JavaScript siz:

.todo:has(input:checked) .title {
  text-decoration: line-through;
  color: #94a3b8;
}

/* Butun sahifa — qorong'i rejim tugmasi checkbox bo'lsa */
:root:has(#dark-mode:checked) {
  --bg: #0b0f19;
  --matn: #f1f5f9;
}

Miqdorga qarab layout:

/* 5 va undan ko'p element bo'lsa — ikki ustun */
.list:has(> :nth-child(5)) {
  columns: 2;
}

Oldingi qo'shni (4-dars: kombinatorlar faqat oldinga qarardi):

h2:has(+ p) {
  margin-bottom: 8px;      /* darhol keyin paragraf kelgan sarlavha */
}

Modal ochiq bo'lsa — sahifa scroll bo'lmasin:

body:has(dialog[open]) {
  overflow: hidden;
}

Ehtiyotkorlik

  • :has() ni keng selektorlarda (body:has(...), *:has(...)) haddan tashqari ishlatmang — har bir DOM o'zgarishida brauzer ko'p elementni qayta tekshirishi mumkin. Aniq konteynerlarga bog'lang
  • :has() ichida yana :has() ishlatib bo'lmaydi
  • Barcha zamonaviy brauzerlarda (2023 yildan) ishlaydi

Hammasi birga

/* Bazaviy — oson o'zgartiriladigan */
:where(.prose) :is(h2, h3) {
  margin-top: 2em;
}

/* Rasmsiz va oxirgi bo'lmagan kartochkalar */
.card:not(:has(img)):not(:last-child) {
  border-bottom: 1px solid #e2e8f0;
}

/* Ichida faol havola bo'lgan menyu bo'limi */
.nav-section:has(a[aria-current="page"]) > summary {
  font-weight: 700;
  color: #2563eb;
}

Nima uchun shunday?

Nima uchun :has() ning paydo bo'lishi shunchalik katta voqea hisoblandi?

Chunki u CSS va JavaScript o'rtasidagi chegarani surdi. Oldin "bola holatiga qarab otani o'zgartirish" — checkbox belgilanganda kartochkani, maydon xato bo'lganda guruhni, rasm bo'lganda layoutni — faqat JavaScript bilan qilinardi: hodisani tinglash, class qo'shish va olib tashlash, holatni sinxronlash. Bu kod xatolarga moyil (class qo'shilib, olib tashlanishi unutiladi) va JavaScript yuklanmaguncha ishlamaydi. :has() bilan bunday holatlar deklarativ: "ichida belgilangan checkbox bo'lsa — shunday ko'rin" — brauzer holatni o'zi kuzatadi, hech qachon sinxrondan chiqmaydi. 4-darsda aytilganidek, brauzer muhandislari buni tez ishlatish yo'lini yillar davomida izlashdi — va natija frontend dasturlashda ko'p narsani soddalashtirdi.


Xulosa

Eslab qoling:

  • :is(a, b) — ro'yxatdan istalgani; noto'g'ri selektorni kechiradi; specificity — ichidagi eng yuqorisi
  • :is() ichiga ID qo'ymang — butun qoida og'irlashadi
  • :where(a, b) — xuddi shunday, lekin specificity 0 — reset va kutubxona stillari uchun
  • :not(a, b) — istisno, ro'yxat bilan; li:not(:last-child) — ajratuvchilar
  • :has() — ota selektor: ichidagi narsaga qarab tanlash; oldingi qo'shni ham (h2:has(+ p))
  • Forma holatlari: :has(input:user-invalid); JavaScript siz holatlar: :has(input:checked)
  • :has() ni aniq konteynerlarga bog'lang, body:has() ni haddan oshirmang

Keyingi dars

32-dars: CSS Functions — calc(), min(), max(), clamp()