Функция :where()
помогает писать меньше кода, применять стили ко всему списку и снимает головную боль при использовании CSS reset. В статье разберёмся, как это работает, и посмотрим на примеры использования.
Что это за функция CSS :where()
Как пишут на MDN, :where()
— это псевдокласс, который в качестве аргумента принимает список селекторов и применяет заданные стили к любому элементу из этого списка. Полезен, когда надо укоротить длинный список селекторов.
В CSS часто приходится писать длинный список селекторов, разделённых запятыми, когда к нескольким элементам одновременно применяются одни и те же стили.
Вот что бывает, когда один и тот же стиль применим ко всем тегам <a>
внутри header
, main
и footer
.
header a:hover,
main a:hover,
footer a:hover {
color: green;
text-decoratíon: underline;
}
Выше у нас только три элемента. Но когда их больше, код становится трудно читать и понимать. А ещё он визуально становится некрасивым. Вот тут-то и требуется :where()
.
:where(header, main, footer) a:hover {
color: red;
text-decoratíon: underline;
}
Браузер доходит до такого фрагмента кода. Код отправляет его к селекторам header
, main
, и footer
и ко всем якорям в этих селекторах. Когда пользователь наведет курсор на якорь, браузер применит заданный стиль. В этом случае — red
и underline
. То есть список селекторов можно записать коротко и ясно.
Что можно сделать с :where()
С помощью :where()
можно группироваться элементы разными способами. Можно разместить :where()
в начале, середине или в конце селектора. Допустим, у нас есть списки.
/* first list */
header a:hover,
main a:hover,
footer a:hover {
color: green;
text-decoratíon: underline;
}
/* second list */
article header > p,
article footer > p{
color: gray;
}
/* third list */
.dark-theme button,
.dark-theme a,
.dim-theme button,
.dim-theme a{
color: purple;
}
А вот то же самое, сделанное с :where()
.
/* first list */
/* at the beginning */
:where(header, main, footer) a:hover {
color: red;
text-decoratíon: underline;
}
/* second list */
/* in the middle */
article :where(header, footer) > p {
color: gray;
}
/* third list */
/* at the end */
.dark-theme :where(button, a) {
color: purple;
}
В первом списке мы указываем, что red
и underline
должны применяться к header
, main
, и footer
при наведении курсора.
Во втором списке указываем, что содержание, заголовок и футер окрашены в серый.
В третьем списке мы указываем, что для button
и a
задали стили .dark-theme
и purple
.
Упростим ещё.
/* at the end */
.dim-theme :where(button, a) {
color: purple;
}
И ещё.
/* stacked */
:where(.dark-theme, .dim-theme) :where(button, a) {
color: purple;
}
Такой подход к сокращению сложного списка селекторов называется наложением.
Специфичность и :where()
Специфичность — способ, с помощью которого браузеры определяют, какие значения свойств CSS или стили нужно применить к конкретному элементу.
Специфичность :where()
всегда равна нулю. Следовательно, любой элемент, на который нацелена функция, автоматически получает равную нулю специфичность. Поэтому можно легко аннулировать стиль любого элемента, сводя специфичность к нулю. Пример с упорядоченными списками HTML.
<div>
<h2>First list no class</h2>
<ol>
<li>List Item 1</li>
<li>List Item 2</li>
</ol>
</div>
<div>
<h2>Second list with class</h2>
<ol class="second-list">
<li>List Item 1</li>
<li>List Item 2</li>
</ol>
</div>
<div>
<h2>Third list with class</h2>
<ol class="third-list">
<li>List Item 1</li>
<li>List Item 2</li>
</ol>
</div>
Во фрагменте кода есть три упорядоченных списка с двумя элементами в каждом. У второго и третьего списка есть заданный класс, у первого — нет. Без применения стилей каждый список упорядочен по номерам.
Добавляем стили. Для этого используем :where()
, чтобы выбрать все теги <ol>
, к которым применен class
.
:where(ol[class]) {
list-style-type: none;
}
Ниже видим, что :where()
применяется ко второму и третьему списку (с классами), поэтому стиль списков удалён.
Добавляем ещё стили.
:where(ol[class]) {
list-style-type: none;
}
.second-list {
list-style-type: disc;
}
Если применять :where()
только ко второму списку, используя наименование класса, то список отобразится с маркерами. А у третьего списка по-прежнему нет стиля.
Вопрос: разве так и не должно быть, раз новый стиль записан ниже :where()
? Нет, и сейчас разберёмся, почему.
Посмотрим, что произойдёт, если переместить добавленный код в верхнюю часть блока кода и передвинуть :where() вниз.
.second-list {
list-style-type: disc;
}
:where(ol[class]) {
list-style-type: none;
}
Стиль всё ещё не изменился.
Запомним, что у :where()
нулевая специфичность. Вне зависимости от того, где размещён новый код относительно :where()
, специфичность элемента, к которому применяется :where()
, равна нулю, а стили аннулированы. Для наглядности давайте добавим стили второго списка в третий список после :where()
в коде.
.second-list {
list-style-type: disc;
}
:where(ol[class]) {
list-style-type: none;
}
.third-list{
list-style-type: disc;
}
Теперь и второй, и третий список отображаются с маркерами, независимо от размещения кода.
Теперь посмотрим, как :where()
будет работать, если один из элементов выбран с помощью недопустимого селектора.
Forgiving и :where()
Если браузер в CSS не распознает только один селектор в списке, весь список окажется недопустимым. Стиль не будет применяться. Но только если не использовать :where()
.
Если элемент в :where()
содержит недопустимый селектор, никакой стиль к этому элементу применен не будет. Но для остальных элементов стили сработают. Функция :where()
пропустит недопустимый селектор. Поэтому :where()
называют forgiving-селектором, дословно — «прощающим».
Ниже видим селектор :unsupported
— недопустимый для многих браузеров. Но весь код ниже будет успешно распарсен и будет соответствовать селектору :valid
, даже в браузерах, которые не поддерживают :unsupported
.
:where(:valid, :unsupported) {
...
}
А вот пример кода, который будет проигнорирован в браузерах, которые не поддерживают:unsupported
, но при этом поддерживают селектор :valid
.
:valid, :unsupported {
...
}
Где можно и нельзя использовать :where()
Инструмент полезный, но со своими ограничениями. В основном ошибаются из-за нулевой специфичности. Если существует элемент или набор элементов, у которых ни в коем случае не должен поменяться стиль, :where() лучше не использовать. Посмотрим, в каких случаях :where()
нужна и хорошо работает.
Улучшить CSS reset
CSS reset — это загрузка набора правил перед применением любых стилей, чтобы очистить браузер от заданных по умолчанию стилей, и чтобы устранить несоответствия между разными браузерами. CSS reset обычно размещают до собственной таблицы стилей, чтобы эти правила сработали первыми. От сложности или простоты селекторов в CSS reset зависит, насколько трудно будет переопределить исходные стили в коде.
Например, представьте, что мы захотели оформить все якоря на странице зелёным. А потом решили, что в футере все якоря должны быть серыми.
Новый стиль (серый цвет) не применяется из-за особенностей CSS reset. У селектора в CSS reset более высокий порядок специфичности, чем у селектора в коде, который применяет стили только для футера. Но если добавить :where()
в CSS reset, все элементы в CSS reset автоматически приобретут нулевую специфичность. Позже, когда будем менять стили, не надо будет беспокоиться о конфликтах специфичности.
Удалить стили
Функция :where()
помогает удалить или обнулить стили или изменить специфичность элементов. Когда поместим функцию в код, она сработает как reset на минималках.
Минимизировать код
Короткий код легко читать и исправлять. Хороший тон — проверять, можно ли сократить любой код, в котором есть больше двух запятых или больше трёх элементов списка. Эта же стратегия помогает комбинировать два и более селектора: section > header > p > a
.
Функция :where()
помогает работать с CSS resets, удалять стили в любом месте кода и просто делать код красивым и понятным. Пользуетесь?
Комментарии (5)
Finesse
28.03.2022 04:23+5В статье не хватает раздела про поддержку браузерами. Сейчас это:
Браузеры на движке Blink (Chromium) версии 88 и новее
Браузеры на движке Gecko (Firefox) версии 78 и новее
Браузеры на движке WebKit (Safari) версии 14 и новее
Согласно “Can I use” это 88.7% пользователей в мире.
csprog
28.03.2022 09:24+1Кратко и по делу. Спасибо.
Ползуюсь :where() На мой взгляд код типа:
:where(.dark-theme, .dim-theme) :where(button, a) { color: purple; }
лучше не использовать, усложняет понимание, что конкретно и каким образом меняется.
Сократит до вот такого достаточно.
.dark-theme :where(button, a) { color: purple; }
.dim-theme :where(button, a) { color: purple; }
hackteck
28.03.2022 09:25Спасибо за статью, весьма полезно, очень не хватало этого селектора.
P.S. ИМХО, если проект позволяет, то лучше смотреть в сторону препроцессоров (SCSS например). На ум приходит сходство, что когда есть выбор, то всегда буду использовать TypeScript вместо JavaScript.
Farmatique
Спасибо, никогда не использовал, буду знать. Надо будет ещё глянуть на поддержку этого псевдосеолектора в canIuse