Сучасний CSS — Нове покоління селекторів (has, where, is, of)

pic

Фото: Вадим Богулов на Unsplash

Як я зазначав у своїй першій статті, я думав поговорити про псевдоклас :has(), але вирішив створити більш обширну серію статей про нові можливості CSS. У цій першій статті я хочу розповісти про нововведення серед псевдокласів.

Насправді, жодна з цих можливостей не є абсолютно новою. CSS Selectors Level 4 було анонсовано ще у 2018 році, але для широкої підтримки в браузерах нам довелося чекати до кінця 2024 року. За актуальними даними, станом на грудень 2024 року підтримка цього стандарту досягла 95%. Тож, якщо вам не потрібно підтримувати старі пристрої, ці вибірки можна використовувати вже зараз.

У цій статті я хочу розглянути вибірки :has(), :where(), :is() та аргумент of. Я дам деякі технічні пояснення, але головною метою є зрозуміти, на що вони здатні на практиці та яке завдання вони вирішують.

[

Selectors Level 4

W3C Робочий проект, Більше деталей про цей документ. Цей розділ не є обов'язковим. Вибірка є булевим предикатом…

www.w3.org

](https://www.w3.org/TR/selectors-4/?source=post_page-----46a5a6a2a445--------------------------------)

:has( )

Цей функціональний псевдоклас пропонує вирішення проблеми вибору parent елементів і попередніх сусідів.

Щоб краще зрозуміти, як це працює, давайте розглянемо приклад. Візьмемо вираз p:has(> h1). Щоб зрозуміти, що це означає, потрібно поміняти місцями основний елемент (в даному випадку p) і вираз аргумента (> h1). Таким чином, ми отримуємо запит p > h1, і якщо він співпадає з будь-яким елементом на сторінці, то вибирається основний елемент (p). Тому вибірка p:has(> h1) означає:

Вибрати всі елементи p, які містять у себе елемент h1, що йде безпосередньо після них.

Отже, ми отримуємо доступ до parent елемента, починаючи з h1. Іншими словами, цей псевдоклас використовується для вибору всіх елементів p, які безпосередньо містять елемент h1.

p:has(> h1) {  
 /* всі p елементи, що містять "непосередньо" елемент h1 */  
}

Для закріплення матеріалу давайте розглянемо ще один приклад. Припустимо, ми хочемо застосувати інший стиль до заголовків (h1), що знаходяться відразу перед абзацами (p). Тобто, ми шукаємо такі h1, після яких йде елемент p.

Вибрати всі елементи h1, перед якими йде елемент p.

h1:has(+ p) {  
 /* всі h1 елементи, перед якими йде елемент p */  
}

Логічні операції: і, або

Щоб перевірити, чи правильно хоча б один з кількох аргументів, потрібно відокремити їх комами. Наприклад, ось як вибрати елементи p, що містять або h1, або h2:

p:has(h1, h2) {  
 /* стилі застосовуються до p, що містять або h1, або h2 */  
}

Щоб вибрати елементи p, які містять обидва елементи — і h1, і h2, потрібно використати два рази псевдоклас :has().

p:has(h1):has(h2) {  
 /* стилі застосовуються до p, що містять і h1, і h2 */  
}

Вибір батьківського елемента

Якщо ми хочемо вибрати перший елемент, що містить .child, це можна зробити за допомогою :has(). Однак, якщо використовувати вираз :has(.child), то вибиратимуться всі parent елементи, що містять .child. Натомість ми хочемо вибрати лише перший parent елемент, що містить .child. Для цього нам необхідно використати комбінатор для нащадків (>):

:has(> .child) {  
 /* ...
*/  
}

Доступ до попередніх сусідів

Ми хочемо вибрати всі елементи .box, що передують елементу .circle.

pic

Вибір всіх попередніх сусідів

Іншими словами, ми вибираємо всі елементи .box, що знаходяться до елементів .circle серед їхніх наступних сусідів. Запит для цього можна сформулювати так:

Вибрати всі елементи .box, що мають принаймні один елемент, який відповідає запиту .circle.

.box:has(~ .circle) {  
 /* ... */  
}

👉 Для більш детальних прикладів вибору сусідніх елементів, будь ласка, ознайомтесь з моєю попередньою статтею.

Обмеження за продуктивністю

Дивлячись на наведену вище інформацію, використання :has() може виглядати дещо заплутано і цікаво. Для розробників це може бути так, але варто знати, що для браузерів виконання вибірки :has() може бути "важким" завданням. Особливо на великих DOM-деревах використання :has() може створювати додаткові витрати на продуктивність. Коли такі вибірки використовуються з складними умовами, час завантаження сторінки може зрости. Якщо DOM оновлюється динамічно, перерахунок вибірок :has() може спричинити проблеми з продуктивністю. Тому при використанні цих вибірок варто дотримуватися кількох рекомендацій:

  • Використовуйте :has() тільки для вирішення таких задач, які неможливо вирішити іншими способами.
  • Використовуйте їх не для всієї сторінки, а для конкретних частин.
  • Періодично перевіряйте час завантаження за допомогою вкладки "Performance" (Продуктивність).

Нарешті, для збереження продуктивності, не можна використовувати :has() всередині іншого :has() або будь-якого іншого псевдоелемента.

[

:has() - CSS: Каскадні таблиці стилів | MDN

Функціональний псевдоклас :has() представляє елемент, якщо будь-який з відносних вибірників, переданих як аргументи, відповідає …

developer.mozilla.org

](https://developer.mozilla.org/en-US/docs/Web/CSS/:has?source=post_page-----46a5a6a2a445--------------------------------)

:is( ) і :where( )

Псевдокласи :is() і :where() приймають список вибірників, розділений комами, і вибирають будь-який елемент, що відповідає хоча б одному з цих вибірників. Вони можуть бути використані з двома основними цілями.

Групування вибірників

Це дуже корисно, щоб зробити список вибірників більш зрозумілим і керованим. Наприклад, якщо ви хочете застосувати однаковий стиль до всіх заголовків h1, що використовуються в різних контекстах:

section h1,  
article h1,  
aside h1,  
nav h1 {  
 font-size: 25px;  
}  

:is(section, article, aside, nav) h1 {  
 font-size: 25px;  
}

Обидва варіанти дають той самий результат, але використання :is() робить код коротшим і зрозумілішим. Ось приклад, де застосування :is() дозволяє уникнути написання 48 вибірників (4 x 4 x 3) і знижує ймовірність помилок при оновленнях.

:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) :is(ul, menu, dir) {  
 list-style-type: square;  
}

Ігнорування недійсних вибірників

Коли ви використовуєте :where() або :is(), якщо один з елементів у списку аргументів є недійсним або не підтримується браузером, то ці аргументи ігноруються, а правило застосовується тільки до тих аргументів, які є дійсними.

Розглянемо наступний приклад. На відміну від попередніх, тут немає жодних комбінацій вибірників. Обидва визначення містять однакову кількість вибірників для однієї й тієї ж операції. З цього погляду використання :is() (або :where) може виглядати надмірним.

:valid,  
:unsupported {  
 /* … */  
}  

:is(:valid, :unsupported) {  
 /* … */  
}

У цьому випадку перше стилістичне правило не буде виконано в браузерах, що не підтримують псевдоклас :unsupported. Друге стилістичне правило буде виконане навіть у разі, якщо :unsupported не підтримується, оскільки воно буде працювати для :valid.
Dolayısıyla bu şekilde “hata toleransı” gerektiren durumlar için de kullanabilirsiniz.

:where( ) і :is( ) різниця

Різниця між ними полягає в обчисленні специфічності (specificity):

  • Функція :is() має специфічність, що відповідає найконкретнішому вибірнику серед аргументів.
  • Функція :where() має специфічність 0.

У наступному прикладі показано, як змінюється специфічність при використанні :is() і :where().

:is(#id-1, .class-1) :is(p, .class-2) h1 {  
 /* SPECIFICITY: 1,1,1 */  
}  

:where(#id-1, .class-1) :where(p, .class-2) h1 {  
 /* SPECIFICITY: 0,0,1 */  
}

У випадку виразу з :is, специфічність 1,1,1, тому що впливають вибірники #id-1 та .class-2. Коли використовується :where, специфічність дорівнює 0,0,1. Це означає, що інший вибірник може легко перекрити цей стиль.

Щоб краще зрозуміти цю потребу, розглянемо наступний приклад. Тут визначений стиль, що має діяти на всьому сайті.

* {  
 box-sizing: border-box;  
}

Як відомо, вибірник * (universal selector) має специфічність нуль. Тому цей стиль можна легко перекрити, якщо це буде потрібно.

Розглянемо ще один приклад. Знову ми хочемо визначити стиль, що буде діяти на всьому сайті і може бути перекритий, але на цей раз додано виключення для класів .nav.

*:not(.nav) {  
 box-sizing: border-box;  
}

На відміну від попереднього прикладу, специфічність у цьому випадку дорівнює 0,1,0, і для того, щоб перекрити цей стиль, знадобиться більше зусиль.

В таких випадках використання псевдокласу :where() може спростити завдання:

*:where(:not(.nav)) {  
 box-sizing: border-box;  
}

Чи буде ця функція корисною, покаже час. Наразі можна сказати, що вона стане в пригоді для CSS-бібліотек або файлів reset.css.

аргумент of

Псевдокласи :nth-child() та :nth-last-child() надають можливість фільтрувати передані їм аргументи. Синтаксис nth-child(An+B [of S]?) дозволяє вибирати елементи з певного підмножини. Тому вирішується проблема, яку я описував в першому написі пошукова фільтрація.

Щоб зрозуміти, як це працює, давайте розглянемо приклад:

Зебра Патерн

У наступній таблиці елементів ми хочемо, щоб видимі рядки мали чергування світлого і темного фону.


 ```  Спочатку вибираємо видимі рядки: `:not(.hidden)`. Потім, з цієї вибірки, за допомогою `:nth-child(even of :not(.hidden))` вибираємо тільки парні рядки. Рішення виглядає так:  ``` tr:nth-child(even of :not(.hidden)){    background-color: lightgray;   } ```  [  ## :nth-child() - CSS: Каскадні таблиці стилів | MDN  ### Псевдоклас :nth-child() CSS вибирає елементи на основі їх індексів у списку дочірніх елементів їхнього батьківського елемента…  developer.mozilla.org  ](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child?source=post_page-----46a5a6a2a445--------------------------------#using_of_selector)  CSS Selectors Level 4 приніс нові можливості, які дозволяють розробникам значно спростити багато проблем, які раніше доводилось вирішувати за допомогою JavaScript або складних підходів. У цій статті ми розглядали властивості, такі як `:has()`, `:where()`, `:is()` та `of`, які розширюють можливості CSS вибірників, покращуючи як читабельність, так і гнучкість коду.  У наступному розділі ми продовжимо знайомитися з нововведеннями CSS, які змінюють уявлення про стиль.
Тримайтеся на зв'язку!

- [https://blog.logrocket.com/advanced-guide-css-has-selector](https://blog.logrocket.com/advanced-guide-css-has-selector)
- [https://blog.logrocket.com/deep-dive-css-where-is-functions/](https://blog.logrocket.com/deep-dive-css-where-is-functions/)



Перекладено з: [Modern CSS — Yeni Nesil Seçiciler (has, where, is, of)](https://medium.com/dogustech/modern-css-1-has-where-is-of-46a5a6a2a445)

Leave a Reply

Your email address will not be published. Required fields are marked *