Подробности CSS-селектора :is(), о которых вы, возможно, не знали

Подробности CSS-селектора :is(), о которых вы, возможно, не знали

Использовать :is() или не использовать :is()?

Рассмотрим следующие сложные селекторы:

.a .b .c {
  background: green;
}
.a :is(.b .c) {
  background: green;
}

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

Попробуйте сами

В демо ниже есть две вложенные структуры разметки:

<div class="a">
 <div class="b">
  <div class="c"></div>
 </div>
</div>

<div class="b">
 <div class="a">
  <div class="c"></div>
 </div>
</div>

Используйте выпадающий список, чтобы увидеть, какие элементы соответствуют селекторам .a .b .c и .a :is(.b .c).

При выборе .a .b .c будет выбрана первая .c. Но при выборе .a :is(.b .c) вы заметите, что обе .c выбираются… и для многих это очень неожиданно.

.a .b .c
Этот селектор состоит из 3 частей: .a, .b и .c. При попытке найти подходящие элементы браузер сначала выберет все элементы .c и затем проверит, есть ли у них родитель с классом .b. Если это так, он затем проверит, является ли этот .b дочерним элементом .a.

.a :is(.b .c)
Этот селектор состоит из 2 частей: .a и :is(.b .c). Первая часть, которая проверяется, это :is(.b .c), и она совпадает с элементами .c, у которых есть предок с классом .b. Если это так, браузер продолжит проверку и удостоверится, что у найденного элемента .c также есть предок с классом .a.

Таким образом, .a :is(.b .c) выберет все элементы .c в примере выше, так как селектор переводится как «найди элементы .c, у которых есть предки с классами .b и .a». Это означает, что будет выбран и этот элемент .c:

<div class="a b">
 <div class="c"></div>
</div>

Если вы не можете это понять, знайте, что .a :is(.b .c) по сути разворачивается в такой набор селекторов:

  • .a .b .c
  • .b .a .c
  • .a.b .c

Почему это важно?

Хотя я сам не стал бы так писать селектор, это становится весьма актуальным из-за CSS Nesting, который сейчас специфицируется. В этом случае селектор вложенности (&) разворачивается в :is().

Рассмотрим этот вложенный блок:

.b .c {
  .a & {
    background: green;
  }
}

Когда & разворачивается в внешний селектор, обёрнутый в :is(), этот фрагмент превращается в:

.a :is(.b .c) {
  background: green;
}

Это может показаться нелогичным для тех, кто раньше использовал Sass и другие препроцессоры. В Sass & просто заменяется внешним селектором:

.a .b .c {
  background: green;
}

Как показано ранее, они ведут себя по-разному.

Есть ли ещё что-то о :is()?

Как упоминалось в предыдущей статье, вот ещё несколько важных моментов о :is():

  1. Список селекторов внутри :is() прощает ошибки.
  2. Специфичность :is() соответствует наиболее специфичному аргументу.
  3. :is() не работает с селекторами псевдоэлементов (пока).