Фото: Вадим Богулов на 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
.
Вибір всіх попередніх сусідів
Іншими словами, ми вибираємо всі елементи .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)