Применимость нового CSS псевдокласса :has() в проектах
С недавних в CSS ввели очень полезный, но местами неоднозначный псевдокласс :has()
. На данный момент поддержка этого псевдокласса, согласно caniuse.com, пока еще реализована в небольшом количестве браузеров, поэтому в работе могут понадобиться применять постобработчики, для обратной совместимости со старыми браузерами.
В настоящее время стандарт W3C Selectors Level 4, в котором описан псевдокласс :has()
, все еще находится в стадии черновика и поэтому вся концепция работы этого псевдокласса окончательно не стандартизирована. Тем не менее, я думаю, что уже вполне можно изучать данный псевдокласс, т.к. маловероятно что будут вносить какие-либо кардинальные изменения в текущий стандарт.
В чем особенность работы псевдокласса :has()
:has()
- функциональный псевдокласс, принимающий в качестве аргументов список дополнительных (относительно основного) селекторов и который активен в том случае, если какой-либо из дополнительных селекторов соответствует хоть одному элементу.
Несколько примеров:
/* Стили применятся к div у которых есть вложенный на любой глубине img */
div:has(img) {}
/* Стили применятся к div у которых есть вложенный на любой
глубине img или элемент с классом .bg-img */
div:has(img, .bg-img) {}
/* Стили применятся к div, непосредственно внутри которых лежит img */
div:has(> img) {}
/* Стили применятся к div, непосредственно после которого лежит img */
div:has(+ img) {}
Если раньше в селекторах можно было влиять только либо на дочерние элементы, либо на следующие относительно текущего соседские элементы (с помощью операторов +
и ~
), то :has()
фактически дает возможность инвертировать обращение.
/* Пример №1: Подсветим блок полей в том случае,
если отмечен хоть один checkbox или radiobutton */
fieldset:has(input:checked) {
background: lightgreen;
}
/* Пример №2: При наведении на любое изображение, для фокусировки
на изображении, текст в документе сделаем полупрозрачным */
:root {
color: rgba(255, 255, 255, 1);
}
:root:has(img:hover) {
color: rgba(255, 255, 255, 0.5); /* делаем полупрозрачным текст */
}
Примеры очень примитивные, но они уже дают понять, что :has()
открывает огромные простор для различных CSS трюков, позволяющих обходится без JavaScript.
Должен с сожалением отметить также и тот факт, что злоупотребление возможностями :has()
может сделать любую верстку трудно поддерживаемой, особенно если в ней отсутствует хоть какая-то логика наследования. Поэтому настойчиво рекомендую составить единый свод правил (styleguide) которому следует верстка, описать в нем четкие правила применения :has()
и только после этого этот псевдокласс применять.
Также хочу уточнить, что :has()
имеет некоторые нюансы:
:has()
не может быть вложенным, т.е. конструкции вида:root:has(a:has(img))
не валидны и не будут работать.- в стандарте отсутствуют псевдоэлементы (
:after
,:before
и пр.), разрешенные к использованию внутри:has()
, но при этом многие псевдоклассы (:hover
,:focus
и пр.) вполне допустимы.
Также стоит открытым вопрос производительности работы этого псевдокласса в браузерах, ведь его логика работы вполне может негативно влиять на возможные браузерные оптимизации. Сейчас еще рано об этом говорить ибо этот псевдокласс совсем недавно появился, но потом этот вопрос может станет актуальным.
JavaScript и псевдокласс :has()
Как ни странно, но данный псевдокласс вполне можно использовать в методах .querySelector()
и .querySelectorAll()
, что открывает большие возможности. Например, если мы хотим получить список всех ссылок (тег <a>
), которые содержат внутри себя изображения (теги <img>
, <picture>
, <svg>
), то нам достаточно применить в запросе :has()
как на примере ниже:
// Запрос всех ссылок с изображениями (img, picture, svg)
document.querySelectorAll('a:has(img, picture, svg)');
Данный пример работает далеко не везде, но в новых Chromium-based браузерах уже вполне поддерживается. Ждем, когда остальные браузеры подтянутся.
(Дополнение от 24.08.2023): А пока вы можете попробовать использовать этот полифил, добавляющий методам .querySelector(), .querySelectorAll(), .matches(), .closest(), поддержку псевдокласса :has(); и про разработку которого я опубликовал отдельную статью.
Заключение
Когда я впервые узнал про этот псевдокласс, я честно говоря ужаснулся от того, насколько CSS может стать тяжелым в плане производительности. С другой стороны, это очень мощный псевдокласс, который может в современную верстку внести как много пользы, так и полнейшую анархию, в случае злоупотребления им. Поэтому все более актуальным становится разработка и использование четко регламентированных правил использования CSS (styleguide) в проектах.
На мой взгляд, этот псевдокласс даст много возможностей при работе с сторонними библиотеками. Например какой-нить сторонний слайдер (например Swiper) может при прокручивании, менять дата-атрибуты, а ваш CSS уже может на это реагировать через :has([data-attr]){}
, при этом вам не придется будет закапываться в недра JS-библиотеки и пр. В общем прикольно, но главное не забывать использовать эту возможность разумно.