Руководство по недавно поддерживаемым современным селекторам псевдоклассов CSS

Опубликовано: 2022-03-10
Краткий обзор ↬ Проект редактора рабочей группы CSS для селекторов уровня 4 включает несколько селекторов псевдоклассов, которые уже имеют варианты предложений в большинстве современных браузеров. В этом руководстве рассматриваются те, которые в настоящее время имеют наилучшую поддержку, а также примеры, демонстрирующие, как вы можете начать использовать их уже сегодня!

Селекторы псевдокласса — это те, которые начинаются с символа двоеточия « : » и соответствуют состоянию текущего элемента. Состояние может быть связано с деревом документов или в ответ на изменение состояния, например :hover или :checked .

:any-link

Несмотря на то, что этот псевдокласс определен в Selectors Level 4, он уже довольно давно поддерживает кроссбраузерность. Псевдокласс any-link будет соответствовать якорной гиперссылке, если у нее есть href . Это будет эквивалентно одновременному совпадению :link и :visited . По сути, это может уменьшить ваши стили на один селектор, если вы добавляете основные свойства, такие как color , которые вы хотели бы применить ко всем ссылкам, независимо от их статуса посещения.

 :any-link { color: blue; text-underline-offset: 0.05em; }

Важное замечание о специфичности заключается в том, что :any-link выиграет у a в качестве селектора, даже если a расположен ниже в каскаде, поскольку он имеет специфичность класса. В следующем примере ссылки будут фиолетовыми:

 :any-link { color: purple; } a { color: red; }

Поэтому, если вы вводите :any-link , имейте в виду, что вам нужно будет включить его в экземпляры a в качестве селектора, если они будут напрямую конкурировать за специфичность.

:focus-visible

Могу поспорить, что одним из самых распространенных нарушений доступности в Интернете является удаление outline интерактивных элементов, таких как ссылки, кнопки и входные данные формы, для их состояния :focus . Одной из основных целей этого outline является служить визуальным индикатором для пользователей, которые в основном используют клавиатуру для навигации. Видимое состояние фокуса имеет решающее значение в качестве инструмента поиска пути, когда эти пользователи перемещаются по интерфейсу, и помогает усилить то, что является интерактивным элементом. В частности, видимый фокус описан в Критерии успеха WCAG 2.4.11: Внешний вид фокуса (минимум).

Псевдокласс :focus-visible предназначен для отображения кольца фокуса только тогда, когда пользовательский агент с помощью эвристики определяет, что оно должно быть видимым. Другими словами: браузеры будут определять, когда применять :focus-visible , основываясь на таких вещах, как метод ввода, тип элемента и контекст взаимодействия. В целях тестирования на настольном компьютере с клавиатурой и мышью вы должны видеть стили :focus-visible , прикрепленные при переходе к интерактивному элементу, но не при щелчке по нему, за исключением ввода текста и текстовых областей, которые должны отображать :focus-visible для всех типов ввода фокуса.

Примечание . Дополнительные сведения см. в рабочем черновике спецификации :focus-visible .

Последние версии браузеров Firefox и Chromium, похоже, теперь обрабатывают :focus-visible при вводе формы в соответствии со спецификацией, в которой говорится, что UA должен удалять стили : :focus при совпадении :focus-visible . Safari еще не поддерживает :focus-visible , поэтому нам нужно убедиться, что стиль :focus включен в качестве запасного варианта, чтобы избежать удаления outline для обеспечения доступности.

Еще после прыжка! Продолжить чтение ниже ↓

Имея кнопку и ввод текста со следующим набором стилей, давайте посмотрим, что произойдет:

 input:focus, button:focus { outline: 2px solid blue; outline-offset: 0.25em; } input:focus-visible { outline: 2px solid transparent; border-color: blue; } button:focus:not(:focus-visible) { outline: none; } button:focus-visible { outline: 2px solid transparent; box-shadow: 0 0 0 2px #fff, 0 0 0 4px blue; }

Хром и Фаерфокс

  • input
    Корректно удалите стили :focus , когда элементы фокусируются с помощью мыши, в пользу :focus-visible что приводит к изменению border-color и скрытию outline при вводе с клавиатуры.
  • button
    Не только использует :focus-visible без дополнительного правила для button:focus:not(:focus-visible) , которое удаляет контур на :focus , но и разрешает видимость box-shadow только при вводе с клавиатуры

Сафари

  • input
    Продолжает использовать только стили :focus
  • button
    Кажется , теперь это частично соответствует назначению :focus-visible на кнопке, скрывая стили :focus при нажатии, но по-прежнему показывая стили :focus при взаимодействии с клавиатурой.

Поэтому на данный момент рекомендуется продолжать включать стили :focus , а затем постепенно расширять до использования :focus-visible , что позволяет демонстрационный код. Вот CodePen для продолжения тестирования:

См. Pen [Тестирование приложения :focus-visible] (https://codepen.io/smashingmag/pen/MWJZbew) Стефани Эклс.

См. приложение Pen Testing: focus-visible от Стефани Эклз.

:focus-within

Псевдокласс :focus-within поддерживается всеми современными браузерами и действует почти как родительский селектор, но только в очень специфических условиях. При прикреплении к содержащему элементу и совпадению дочернего элемента с :focus стили могут быть добавлены к содержащему элементу и/или любым другим элементам внутри контейнера.

Практическим усовершенствованием для использования этого поведения является стилизация метки формы, когда связанный ввод имеет фокус. Чтобы это работало, мы помещаем метку и ввод в контейнер, а затем присоединяем :focus-within к этому контейнеру, а также выбираем метку:

 .form-group:focus-within label { color: blue; }

Это приводит к тому, что метка становится синей, когда ввод имеет фокус.

Эта демонстрация CodePen также включает добавление схемы непосредственно в .form-group :

См. Pen [Тестирование применения :focus-within](https://codepen.io/smashingmag/pen/xxgmREq) Стефани Эклс.

См. приложение Pen Testing :focus-within от Стефани Эклс.

:is()

Также известный как псевдокласс «сопоставляется с любым», :is() может принимать список селекторов для сопоставления. Например, вместо того, чтобы перечислять стили заголовков по отдельности, вы можете сгруппировать их под селектором :is(h1, h2, h3) .

Пара уникальных особенностей списка селекторов :is() :

  • Если указанный селектор недействителен, правило будет по-прежнему соответствовать допустимым селекторам. Учитывая :is(-ua-invalid, article, p) , правило будет соответствовать article и p .
  • Вычисленная специфичность будет равна специфичности переданного селектора с наивысшей специфичностью. Например, :is(#id, p) будет иметь специфичность #id — 1.0.0 — тогда как :is(p, a) будет иметь специфичность 0.0.1.

Ключевым преимуществом является игнорирование недопустимых селекторов. При использовании других селекторов в группе, где один селектор недействителен, браузер отбрасывает все правило. Это вступает в игру в нескольких случаях, когда префиксы поставщиков все еще необходимы, а группировка селекторов с префиксом и без префикса приводит к сбою правила во всех браузерах. С помощью :is() вы можете безопасно сгруппировать эти стили, и они будут применяться, когда они совпадают, и игнорироваться, когда они не совпадают.

Для меня группировка стилей заголовков, как упоминалось ранее, уже является большой победой с этим селектором. Это также тот тип правила, который мне было бы удобно использовать без запасного варианта при применении некритичных стилей, таких как:

 :is(h1, h2, h3) { line-height: 1.2; } :is(h2, h3):not(:first-child) { margin-top: 2em; }

В этом примере (который исходит из стилей документа в моем проекте SmolCSS) большая line-height унаследованная от базовых стилей, или отсутствие margin-top на самом деле не является проблемой для неподдерживающих браузеров. Это просто далеко от идеала. Для чего вы не хотели бы использовать :is() , так это для критически важных стилей макета, таких как Grid или Flex, которые значительно контролируют ваш интерфейс.

Кроме того, при привязке к другому селектору вы можете проверить, соответствует ли базовый селектор селектору-потомку внутри :is() . Например, следующее правило выбирает только те абзацы, которые являются прямыми потомками статей. Универсальный селектор используется как ссылка на базовый селектор p .

 p:is(article > *)

Для лучшей текущей поддержки, если вы хотите начать использовать ее, вы также захотите удвоить стили , включив повторяющиеся правила, используя :-webkit-any() и :matches() . Не забудьте сделать эти индивидуальные правила, иначе даже поддерживающий браузер их выбросит! Другими словами, включите все нижеперечисленное:

 :matches(h1, h2, h3) { } :-webkit-any(h1, h2, h3) { } :is(h1, h2, h3) { }

На этом этапе стоит упомянуть, что наряду с самими новыми селекторами есть обновленная @supports , которая является @supports selector . Это также доступно как @supports not selector .

Примечание . В настоящее время (из современных браузеров) только Safari не поддерживает это правило.

Вы можете проверить наличие поддержки :is() с помощью чего-то вроде следующего, но на самом деле вы потеряете поддержку Safari, поскольку Safari поддерживает :is() , но не поддерживает @supports selector .

 @supports selector(:is(h1)) { :is(h1, h2, h3) { line-height: 1.1; } }

:where()

Псевдокласс :where() почти идентичен :is() за исключением одного важного отличия: он всегда будет иметь нулевую специфичность. Это имеет невероятные последствия для людей, которые создают фреймворки, темы и дизайн-системы . Используя :where() , автор может установить значения по умолчанию, а последующие разработчики могут включать переопределения или расширения без конфликта специфики.

Рассмотрим следующий набор стилей img . Используя :where() , даже с селектором более высокой специфичности специфичность остается нулевой. Как вы думаете, в следующем примере граница какого цвета будет у изображения?

 :where(article img:not(:first-child)) { border: 5px solid red; } :where(article) img { border: 5px solid green; } img { border: 5px solid orange; }

Первое правило имеет нулевую специфичность, так как оно полностью содержится в :where() . Таким образом, непосредственно против второго правила выигрывает второе правило. Представляя селектор только элемента img в качестве последнего правила, он выиграет из-за каскада. Это связано с тем, что оно будет вычисляться с той же специфичностью, что и правило :where(article) img , поскольку часть :where() не увеличивает специфичность.

Использование :where() вместе с резервными вариантами немного сложнее из-за функции нулевой специфичности, поскольку эта функция, вероятно, является причиной того, что вы захотите использовать ее поверх :is() . И если вы добавите резервные правила, они, скорее всего, превзойдут :where() из-за самой своей природы. И он имеет лучшую общую поддержку , чем @supports selector поэтому попытка использовать его для создания резервного варианта вряд ли принесет большую (если вообще) выгоду. По сути, помните о невозможности правильно создать запасные варианты для :where() и тщательно проверьте свои собственные данные, чтобы определить, безопасно ли начинать использовать для вашей уникальной аудитории.

Вы можете дополнительно протестировать :where() с помощью следующего CodePen, который использует селекторы img , указанные выше:

См. Pen [Тестирование специфичности `:where()`] (https://codepen.io/smashingmag/pen/jOyXVMg) Стефани Эклс.

См. специфику Pen Testing :where() Стефани Эклс.

Улучшено :not()

Базовый селектор :not() поддерживается начиная с Internet Explorer 9. Но селекторы уровня 4 улучшают :not() , позволяя ему принимать список селекторов, точно так же, как :is() и :where() .

Следующие правила дают тот же результат в поддерживаемых браузерах:

 article :not(h2):not(h3):not(h4) { margin-bottom: 1.5em; } article :not(h2, h3, h4) { margin-bottom: 1.5em; }

Способность :not() принимать список селекторов отлично поддерживается современными браузерами.

Как мы видели с :is() , расширенный :not() также может содержать ссылку на базовый селектор как на потомка с использованием * . Этот CodePen демонстрирует эту возможность, выбирая ссылки, которые не являются потомками nav .

См. Pen [Тестирование :not() с помощью селектора потомков] (https://codepen.io/smashingmag/pen/BapvQQv) Стефани Эклс.

См. Pen Testing :not() с селектором потомков Стефани Эклс.

Бонус : предыдущая демонстрация также включает пример объединения :not() и :is() для выбора изображений, которые не являются соседними братьями и сестрами элементов h2 или h3 .

Предлагается, но «под угрозой» — :has()

Последний псевдокласс, который является очень захватывающим предложением, но не имеет текущего браузера, реализующего его даже экспериментально, — это :has() . На самом деле, в редакционном черновике Selector уровня 4 он указан как «находящийся под угрозой», что означает, что у него есть трудности с завершением его реализации, и поэтому он может быть исключен из рекомендации.

В случае реализации :has() по существу станет «родительским селектором», о котором мечтают многие специалисты по CSS. Это будет работать с логикой, похожей на комбинацию :focus-within и :is() с селекторами потомков, где вы ищете наличие потомков , но применяется стиль к родительскому элементу.

Учитывая следующее правило, если бы навигация содержала кнопку, то верхняя и нижняя навигация уменьшила бы отступы:

 nav { padding: 0.75rem 0.25rem; nav:has(button) { padding-top: 0.25rem; padding-bottom: 0.25rem; }

Опять же, в настоящее время это не реализовано ни в одном браузере даже экспериментально, но об этом интересно подумать! Робин Рендл предоставил дополнительную информацию об этом будущем селекторе на CSS-Tricks.

Почетное упоминание с уровня 3: :empty

Полезный псевдокласс, который вы, возможно, пропустили в селекторах уровня 3, — это :empty , который соответствует элементу, когда у него нет дочерних элементов, включая текстовые узлы.

Правило p:empty будет соответствовать <p></p> , но не <p>Hello</p> .

Один из способов использования :empty — скрыть элементы, которые, возможно, являются заполнителями для динамического содержимого, заполненного с помощью JavaScript. Возможно, у вас есть div, который будет получать результаты поиска, и когда он будет заполнен, он будет иметь рамку и некоторые отступы. Но пока нет результатов, вы не хотите, чтобы он занимал место на странице. Используя :empty , вы можете скрыть его с помощью:

 .search-results:empty { display: none; }

Возможно, вы думаете о добавлении сообщения в пустом состоянии и испытываете искушение добавить его с псевдоэлементом и content . Подводным камнем здесь является то, что сообщения могут быть недоступны для пользователей вспомогательных технологий, которые несовместимы с тем, могут ли они получить доступ к content . Другими словами, чтобы убедиться, что сообщение типа «нет результатов» доступно , вы можете добавить его как реальный элемент, такой как абзац ( aria-label больше не будет доступна для скрытого элемента div).

Ресурсы для изучения селекторов

CSS имеет гораздо больше селекторов, включая псевдоклассы. Вот еще несколько мест, где можно узнать больше о том, что доступно:

  • Документация по селекторам MDN CSS включает исчерпывающий список по категориям;
  • Я написал руководство по расширенным селекторам CSS, состоящее из двух частей, вы можете начать с первой части;
  • Получайте удовольствие от изучения селекторов CSS с помощью игры CSS Diner;
  • Китти Жиродель создала инструмент объяснения селектора, который разбивает и описывает части предоставленного селектора.