Как работает псевдоселектор CSS :has()

CSS :has() — полное руководство по псевдоселектору

30.01.2024
140
24 мин.
0

В течение многих лет селекторы CSS позволяли стилизовать элементы на основе их тегов и классов. Псевдоселектор CSS :has() — долгожданное решение для выбора родительских элементов с помощью атрибутов их дочерних элементов (child).

Во всяком случае, когда-то такой подход считался невозможным без JavaScript. Это новшество решает проблему напрямую, предлагая инновационный способ решения этой задачи.

Прежде всего, в этом руководстве мы рассмотрим этот псевдоселектор, который позволяет стилизовать родительские и целевые дочерние элементы. Однако в нём описывается не только необходимость использования этого новшества, но также предоставляет исчерпывающий обзор возможностей его использования и примеров практического применения. Мы также рассмотрим вопрос его совместимость с браузерами и альтернативы.

Что такое псевдоселектор CSS :has()?

Во-первых, псевдоселекторы (или псевдоклассы) — это просто ключевые слова, которые мы используем в CSS. Они располагаются после основного селектора и позволяют настраивать таргетинг и стилизацию конкретных элементов. В отличие от обычных селекторов, псевдоселекторы применяются для выбора и стилизации элементов на основе их состояния или положения в DOM-дереве. Целевые элементы должны соответствовать определённым критериям выбора.

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

Во-вторых, CSS has — это единственный псевдоселектор, который выбирает элементы на основе того, содержат ли они определённые потомки. Он необходим для выбора родительского элемента с одним или несколькими потомками, которые соответствуют указанному селектору, а также его дочерними элементами.

Он считается функциональным селектором, так как для выбора определённых элементов он принимает параметры. В их число может входить элемент, список элементов или комбинаторы (+, ~, >); которые нужны для указания элементов.

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

Базовый пример селектора CSS :has()

Создадим HTML-разметку. Например:

<div>
    <span class="my-class">This div has a span child element with the class my-class.</span>
</div>
<div>
    <span>
 This div has a span child element, but the span element does not have the class my-class.
   </span>
</div>
<div>
This div does not have any child elements.
</div>
<div>
    <span>This div has a span child element, but it is not the only child element.</span>
    <span>This is another child element.</span>
</div>
<div>
    <span>This div has a span child element, but it is not the only child element.</span>
    <p>This is another child element.</p>
</div>

Приведённый выше HTML-код включает пять элементов div, каждый из которых слегка отличается своими дочерними элементами. Первый div имеет дочерний элемент span с классом my-class. Во втором div — элемент span, но на этот раз без класса. В третьем div нет ни одного дочернего элемента. Четвёртый div имеет два дочерних элемента span. И последний имеет дочерние элементы span и p.

Теперь добавим CSS. Например:

div{
    margin: 3rem;
}
div:has(.my-class) {
    color: rgb(255, 0, 0);
}
div:has(span) {
    color: rgb(0, 0, 255);
}
div:has(*) {
    border: 1px solid rgb(255, 0, 0);
}
div:has(p ){
    font-size: 1.5rem;
}
@supports not (selector(html:has(body))) {
    body:before {
        padding: 1rem;
        color: rgb(255, 0, 0);
        display: block;
        content: "Your browser doesn't support the `:has()` 
        pseudo-class yet.";
    }
}

Для CSS кода мы сначала указываем общие стили HTML-разметки, и создаём margin в 3 rem вокруг каждого div. Используя псевдоселекторы CSS :has(), мы добавили красный цвет каждому элементу div с дочерним элементом с классом my-class. Затем применили красный цвет к всем элементам div с дочерним элементом в виде тега span.

Мы установили border: 1px solid rgb(255, 0, 0) для всех элементов div, у которых есть дочерние элементы. Затем мы применили стиль font-size: 1.5em ко всем элементам div с дочерними элементами в лице тега p.

Наконец, мы использовали @supports, которое подробно обсудим позже, чтобы проверить, поддерживает ли текущий браузер псевдокласс CSS :has(). Если нет, то в браузере будет отображено соответствующее сообщение.

И вот, что у нас получилось в итоге:

Пример использования css has
Простой пример использования псевдоселектора CSS :has()

Синтаксис CSS has

Синтаксис псевдокласса следующий:

Target:has(selector)

Target представляет селектор CSS для элемента, который мы хотим стилизовать. Это может быть любой допустимый селектор CSS, тег, класс или идентификатор. Он может соответствовать одному или нескольким элементам на странице.

Псевдокласс :has() выбирает элементы на основе того, содержат ли они определённый потомок, соответствующий конкретному селектору.

Работа CSS :has() с комбинаторами и другими псевдоселекторами

До сих пор мы рассматривали псевдоселектор CSS :has() в базовом использовании, но комбинаторы продвигают его возможности немного дальше.

Комбинаторы в CSS — это знаки, которые используются для указания взаимосвязи между двумя или более элементами в селекторе. С помощью комбинаторов мы можем выбирать элементы на основе их положения относительно других элементов в структуре HTML.

Существует четыре основных типа комбинаторов:

  • Комбинатор-потомок (пробел). Позволяет получать элементы, вложенные в другие элементы.
  • Дочерний комбинатор (>). Нацелен на элементы, которые являются непосредственными дочерними элементами родительского элемента (только прямые дочерние элементы).
  • Комбинатор смежного элемента (+). Выбирает элемент, который является следующим родственным элементом другого указанного элемента и имеет того же родителя. Он нацелен на элементы, которые расположены сразу после первого элемента.
  • Общий комбинатор родственных элементов (~). Общий комбинатор родственных элементов выбирает элемент, который является родственным другим указанным элементам и имеет того же родителя. Он нацелен на элементы, которые появляются после первого элемента, независимо от их положения.

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

Давайте рассмотрим этот код в качестве примера:

<ul>
    <li>
        <p>First paragraph</p>
        <p>Second paragraph</p>
    </li>
    <li>
       <p>Only one paragraph</p>
    </li>
    <li>
        <p>First paragraph</p>
    <div>
        <p>Another paragraph</p>
    </div>
     <p>Third paragraph</p>
    </li>
</ul>
li:has(p + p) {
    color: rgb(27, 179, 179);
}

В этом примере селектор CSS li:has (p + p) имеет цель в виде li, в котором имеются два последовательных тега p в качестве прямых дочерних элементов. Первый элемент списка содержит два последовательных тега p. Следовательно, он будет оформлен другим цветом, поскольку соответствует этому условию.

Как работает псевдоселектор CSS :has()
Псевдоселектор CSS :has() имеет как свои преимущества, так и недостатки (изображение создано с помощью ИИ)

Совмещение псевдоселекторов :has и :not

Псевдоселектор :not позволяет выбирать элементы, которые не соответствуют определённому селектору. Он исключает определённые элементы на основе определённых критериев. Его можно использовать для создания исключения в CSS-стилях и комбинировать с селектором :has() для определения элементов, которые не соответствуют требуемому селектору.

Давайте воспользуемся им для редактирования нашего предыдущего кода:

li:not(p + p) {
    color: rgb(27, 179, 179);
}

Работа псевдоселектора :has() в диапазоне

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

Чтобы проиллюстрировать, как элементы могут быть выбраны в диапазоне, давайте рассмотрим этот фрагмент кода:

<nav>
    <h3>Browser Testing</h3>
    <ul class="nav-items">
        <li><button>Configure tunnel</button></li>
        <li><button>Upgrade Now</button></li>
    </ul>
</nav>
<ul class="design">
    <h1>Browser testing</h1>
    <h3>Browsers List</h3>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/chrome%20logo.svg" alt=""> Chrome
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/safari%20logo.svg" alt=""> Safari
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/edge%20logo.svg" alt="">Edge
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/internet-explorer%20logo.svg" alt=""> Internet Explorer
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/firefox%20logo.svg" alt="">Firefox
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/brave%20logo.svg" alt=""> Brave
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/opera%20log.svg" width="20px" alt="">Opera
    </li>
<br>
<hr>
<br>
    <h3>Operating System</h3>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/windows-applications%20logo.svg" alt="">windows 11
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/windows-applications%20logo.svg" alt="">windows 10
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/apple%20logo.svg" alt="">MacOS Monterey
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/apple%20logo.svg" alt="">MacOS Catalina
    </li>
    <li>
        <img src="https://raw.githubusercontent.com/Cejay101/LamdaTestImages/34c915228872e5ff257653862c273bd914915a5f/images/apple%20logo.svg" alt="">MacOS Big Sur
    </li>
<br>
<hr>
<br>
    <h3>Resolution</h3>
    <li>1024x768</li>
    <li>1280x720</li>
    <li>1280x800</li>
    <li>1280x1024</li>
 </ul>

Выше у нас есть простая HTML-разметка, в центре внимания которой находится неупорядоченный список, содержащий различные теги элементов. Они будут использоваться для демонстрации того, как выбор в диапазоне работает с псевдоселектором CSS :has().

nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  background-color: rgb(138, 221, 231);
}
a {
  text-decoration: none;
  color: rgb(0,0 ,0);
}
button {
  padding: 0.5rem;
  background-color: inherit;
  border: 1px solid rgb(158, 158, 158);
}
.nav-items {
  list-style: none;
  gap: 2rem;
  display: flex;
  align-items: center;
  background-color: inherit;
} 
h1 {
  text-align: center;
  font-size: 2rem;
  color: rgb(39, 45, 71);
}
.design {
  background-color: rgb(255, 255, 255);
  width: 500px;
  margin: 3rem auto;
  padding: 3rem;
}
h3 {
 margin-bottom: 10px;
}
img {
  width: 30px;
}
li {
  display: flex;
  gap: 10px;
  margin-bottom: 5px;
}
@media screen and (max-width:630px) {
  .design{
    max-width : 90vw;
    padding: 1rem; 
  } 
}
@supports not (selector(html:has(body))) {
  body:before {
    padding: 1rem;
    color: rgb(255, 0, 0);
    display: block;
    content: "Your browser doesn't support the `:has()` pseudo-class yet.";
  }
}

Приведённый выше CSS описывает базовый стиль HTML-разметки.

Ниже приведён полученный результат:

See the Pen
Untitled
by Вячелав Демченко (@upsdjzqs-the-reactor)
on CodePen.

Выбор первого элемента в диапазоне

Как разработчикам, нам могут потребоваться стили элементов, которые непосредственно следуют за определённым тегом. Хороший способ сделать это — использовать CSS-селектор :has() вместе с комбинаторами для выбора любого первого элемента, находящегося в диапазоне.

Давайте добавим некоторый стиль к нашему предыдущему коду, чтобы проиллюстрировать это:

.design h3 + :has(~ h3) {
    background-color:#0ebac5;
    padding: 10px;
    border-radius: 10px;
}

В приведённом выше коде элементы h3, которые являются родственными текущему элементу h3, также представляют собой элементы h3, которые находятся после текущего элемента h3. Используя такую комбинацию, как h3 + :has(~ h3) позволит выбрать все элементы h3, за которыми сразу следует другой элемент h3.

Выбор последнего элемента в диапазоне

Выбор последнего элемента немного похож на выбор первого. Разница заключается в используемых комбинаторах:

.design h3 ~ :has(+ h3) {
    background-color:#0ebac5;
    padding: 10px;
    border-radius: 10px;
}

Сочетание CSS-стилей .design h3 ~ :has(+ h3) позволяет выбрать последний элемент из первых двух h3 в списке, потому что им обоим предшествует другой элемент h3 . Последний элемент третьего элемента h3 не будет выбран, потому что ему не предшествует другой элемент h3:

See the Pen
Untitled
by Вячелав Демченко (@upsdjzqs-the-reactor)
on CodePen.

Выбор родственного элемента в диапазоне

С помощью псевдоселектора :has() можно также выбрать все элементы в определённом диапазоне и применить к ним необходимые стили. Так же, как мы делали с другими выборками родственных элементов :has() будет использоваться наряду с комбинаторами:

.design h3 ~ :has(~ h3) {
    background-color:#0ebac5;
    padding: 10px;
    border-radius: 10px;
}

В приведённом выше коде мы используем .design h3 ~ :has(~h3) для выбора всех дочерних элементов, которые идут после первого h3 и перед последним h3.

Как мы можем видеть, все родственные элементы теперь стилизованы бирюзовым фоном, включая элемент hr:

See the Pen
Untitled
by Вячелав Демченко (@upsdjzqs-the-reactor)
on CodePen.

Давайте попробуем выделение в диапазоне с другим элементом (hr), чтобы по-другому взглянуть на то, как это влияет на стилистику. Например:

.design hr ~ :has(~ hr) {
  background-color:#0ebac5;
  padding: 10px;
  border-radius: 10px;
}

В этом примере мы можем видеть, что каждый элемент получил определённый стиль оформления.

Пседоселекторы для указания родственных элементов

Различные прочие существующие псевдоселекторы также позволяют обращаться к определённым элементам в наборе. Например, псевдоселектор первого дочернего элемента нацелен на первый элемент внутри родительского элемента. В то же время как псевдоселектор последнего дочернего элемента нацелен на последний элемент.

Кроме того, селектор n-го дочернего элемента позволяет точно определить конкретного дочернего элемента или диапазон дочерних элементов на основе их положения. Однако, когда дело доходит до псевдоселектора :has(), есть ключевое различие, которое отличает его от этих других.

Как мы видели выше, он представляет уникальный подход к выбору элементов. В отличие от селекторов первого дочернего элемента и последнего дочернего элемента, которые учитывают исключительно положение элементов внутри их родительского элемента, и селектора n-го дочернего элемента, который зависит от положения элемента, :has() фокусируется на характеристиках дочерних элементов.

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

Ниже представлена таблица чёткого различия каждого из этих псевдоселекторов:

Практический пример CSS :has() при валидации формы

Существуют различные сценарии, в которых селектор :has() может быть очень полезен. Проверка формы — один из них.

При создании форм мы часто используем теги label и input вместе, следовательно, они являются родственными. Чтобы продемонстрировать использование родственного селектора, мы создадим форму, показывающую, когда поля ввода проверены, а когда нет.

<nav>
    <h3>
        <a href="https://www.lambdatest.com/">
            <img class="logo" src="https://www.lambdatest.com/resources/images/logos/logo.svg" alt="logo">
         </a>
    </h3>
    <ul class="nav-items">
        <li>
            <a href="https://www.lambdatest.com/feature">Platform</a> 
        </li>
        <li>
            <a href="https://www.lambdatest.com/enterprise">Enterprise</a>
        </li>
        <li>
            <a href="https://www.lambdatest.com/blog/">Resources</a>
        </li>
        <li>
            <a href="https://www.lambdatest.com/support/docs/getting-started-with-lambdatest-automation/">Developers</a>
        </li>
        <li>
            <a href="https://www.lambdatest.com/pricing">Pricing</a>
        </li>
    </ul>
    <img class="hamburger" src="https://www.lambdatest.com/resources/images/icons/toggle_menu.svg" alt="">
</nav>
<form class="form">
    <h2>Sign Up</h2>
    <p>Please provide your name, email and phone number. 
</p>
    <div class="field">
        <label for="name">Name </label>
        <span>
        <input type="name" id="name" required placeholder="e.g Clinton joy"/>
        </div>
    <div class="field">
        <label for="email">Email Address </label>
        <span>
        <input type="email" id="email" required placeholder="e.g clinton@gmail.com"/>
    </div>
    <div class="field">
        <label for="number">Phone number </label>
        <span>
        <input type="number" id="number" required placeholder="+1 893 389 393"/>
    </div>
    <div>
        <button>Submit</button>
    </div>
    <p>Already have an account? 
        <a class="sign-in" href="https://accounts.lambdatest.com/login?_gl=1*cz6gbh*_gcl_au*MTU1OTQzNjIzMS4xNjg5ODI4OTQ5">Sign In</a>
    </p>
</form>

В приведённом выше HTML-коде мы создали разметку формы регистрации. Страница состоит из раздела простой навигации (nav) и формы (form):

a {
  color: rgb(0, 0, 0);
  text-decoration: none;
}
nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
}
.nav-items {
  list-style: none;
  gap: 2rem;
  display: flex;
}
.hamburger {
  display: none;
}

form{
  background-color: rgb(255, 255, 255);
  border-radius:10px ;
  width: 600px;
  margin: 3rem auto;
  padding: 3rem;
  max-width: 100vw;
}
h2 {
  margin-bottom:0.5rem ;
  font-size: 3rem;
  color: rgb(36, 54, 96);
  font-weight: bold;
}
p {
  font-size: 1.5rem;
  color: rgb(148, 148, 148);
  margin-bottom: 2rem;
}
.field {
  margin-bottom: 1.5rem;
  position: relative;
}
label {
  font-size: 1rem;
  color: rgb(36, 54, 96);
}
input {
  display: block;
  padding:1rem;
  width: 100%;
  font-family: 'Nunito', sans-serif;
  box-sizing: border-box;
  font-size: 1rem;
  border: 1px solid rgb(188, 188, 188);
  border-radius: 5px;
}
button {
  border: none;
  background-color: rgb(36, 54, 96) ;
  color: white;
  padding: 1rem 1.5rem;
  margin-bottom:2rem ;
  border-radius: 5px;
  font-family: 'Nunito', sans-serif;
}
span {
  position: absolute;
  right: 0;
}
span:has(+ input:invalid),
span:has(+ input:invalid)::before {
  content: '✖';
  color:rgb(236, 29, 29);
}
span:has(+ input:valid),
span:has(+ input:valid)::before {
  content: '✓';
  color: rgb(27, 159, 91);
}  
input:focus {
  border: none;
}
input:hover {
  background-color: rgb(36, 54, 96);
}
input:focus:invalid {
  background: rgb(213, 155, 165);
  border: red 2px solid;
  outline: rgb(236, 29, 29);
}  
input:focus:valid {
  border: rgb(35, 152, 66) 2px solid;
  outline: rgb(27, 159, 91);
  background: #c9ffd9;
}
p:has(a) {
  color: rgb(36, 54, 96);
}
.sign-in {
  color: blue;
}
@supports not (selector(html:has(body))) {
  body:before {
    padding: 1rem;
    color: rgb(255, 0, 0);
    display: block;
    content: "Your browser doesn't support the `:has()` pseudo-class yet.";
  }
}
@media screen and (max-width:712px) {
  .hamburger {
    display: block;
    width: 30px;
  }
  .nav-items {
    display: none;
  }
  h2 {
    font-size: 2rem;
  }
  p {
    font-size: 1rem;
  }
  .form {
    width: 90vw;
    max-width: 100vw;
    padding: 1rem;
  }
}

В результате, каждый раздел формы содержит три поля, каждое из которых состоит из тегов label, span и input. Структура составлена таким образом, чтобы мы могли ориентироваться на родственные элементы с помощью селектора :has().

Этот CSS-код включает основные стили, но наше основное внимание сосредоточено на div с классом div. Все элементы внутри этого div являются родственными, и с помощью свойства CSS :has() мы можем успешно выбрать элемент на основе его родственного.

Мы применим псевдоселектор :has() для получения элемента span внутри раздела field. Мы скорректируем содержимое span и применим стили на основе проверки смежного поля input, которое также является родственным.

Как правило, несмотря на наличие нескольких div с классом field, этот метод обеспечивает согласованный стиль содержимого span на основе проверки поля input. Этот подход упрощает взаимодействие с пользователем в разных input.

В итоге, как и ожидалось, содержимое span изменяется:

See the Pen
Untitled
by Вячелав Демченко (@upsdjzqs-the-reactor)
on CodePen.

Актуальность селектора CSS :has()

Псевдоселектор CSS :has() имеет большое значение в мире веб-разработки. Он обеспечивает возможность выборки элементы на основе конкретных дочерних или родственных элементов. Эта возможность обеспечивает новый уровень понимания контекста и точности выбора CSS. Она предлагает лучшее решение для старого способа получения родительских элементов в JavaScript. Тем самым, это устраняет ненужные сложности, с которыми сталкивается метод с использованием JavaScript.

Вот некоторые из основных результатов введения псевдоселектора :has():

Контекстный стиль

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

Гибкий таргетинг

Другим преимуществом селектора :has() является возможность выбирать родительские элементы, соответствующие определённым критериям или их потомками. Это обеспечивает гибкость при выборе элементов. В противном случае потребовались бы сложные комбинации классов или атрибутов.

Уменьшена зависимость от классов

Он способен уменьшить нашу зависимость от добавления дополнительных классов исключительно в целях стилизации. Мы видели это в действии, когда выбирали элементы в диапазоне. Все они были выбраны без необходимости присвоения им классов.

Таким образом, вместо того, чтобы загромождать HTML классами, мы можем использовать взаимосвязь между родительскими и дочерними элементами. Это позволит применять стили, более чётко разделяя контент и обеспечивая лучшую ориентацию в коде.

Псевдоселектор :has(), как правило, актуален для современного веб-дизайна, поскольку предлагает более интуитивный и контекстно-зависимый подход к выбору элементов.

Методологии и приёмы CSS в качестве альтернативы :has()

Ранее мы обсуждали поддержку браузером селектора :has(). Конечно, иногда псевдоселектор :has() недоступен или вам нужно добиться аналогичных эффектов в старых браузерах. В этом случае вы можете использовать методологии и приёмы CSS для достижения аналогичных результатов. С другой стороны существует несколько альтернатив, которые следует рассмотреть:

Выбор на основе классов

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

Манипуляции с JavaScript

В частности, можно использовать JavaScript для добавления классов или атрибутов к родительским элементам на основе наличия определённых потомков. Затем оформите эти элементы с помощью CSS.

Псевдоклассы дочерних элементов

Для того, чтобы применять стили к дочерним элементам на основе их положения или содержимого, потребуются псевдоклассы типа :first-child, :last-child или :nth-child.

Ограничения псевдоселектора :has()

По справедливости говоря, селектор CSS :has() призван революционизировать веб-разработку. Однако он несёт определённые ограничения, которые следует учитывать при его использовании. Некоторые из них включают:

  • Совместимость с браузерами. Это основное ограничение, когда дело доходит до :has(). Хотя он и поддерживается большинством современных браузеров, важно понимать, что он может быть пока совместим не со всеми из них.
  • Влияние на производительность. Одним из заметных ограничений селектора :has() является его потенциальное влияние на производительность. Чтобы идентифицировать все элементы, соответствующие селектору, он проходит через всю иерархию DOM. Это может заметно замедлить процесс рендеринга, особенно на веб-страницах с обширным набором элементов.
  • Проблемы с отладкой. К сожалению, проблемы с отладкой кода, связанные с :has() могут быть сложными. Когда код не даёт ожидаемого результата, может быть трудно найти проблему. Отсутствие конкретики при работе с псевдоселектором CSS :has() затрудняет отладку кода.

Читайте также: Метод Array.from — как создать массив чисел в JavaScript.

Кроссбраузерность

Без всякого сомнения, псевдоселектор :has() — это она из относительно новых CSS свойств. Бесспорно, его поддержка браузерами все ещё развивается. Тем не менее, наиболее популярных браузеры последних версий его воспринимают.

Проверка поддержки CSS :has() браузерами

В связи с тем, что этот псевдоселектор пока поддерживается не во всех браузерах, проверка лишней не будет. Таким образом, мы можем предоставить резервные варианты для браузеров, которые не поддерживают его. На случай этого мы используем @supports.

Кроме всего прочего, в приведённом выше коде первое правило @supports проверяет, поддерживает ли браузер селектор :has(). В этом случае псевдоселектор внутри @supports будет работать. Стоит отметить, что CSS внутри @supports указывает, что элемент body должен иметь определённый стиль. Если у него есть один или несколько дочерних элементов, которые соответствуют требуемую селектору любого элемента или класса.

Второе правило @supports проверяет, поддерживает ли браузер псевдокласс :has(). Если нет, то CSS внутри @supports будет применён блок стилей.

В общей сложности, правило CSS внутри блока @supports указывает, что элемент before должен быть добавлен к элементу body. Элемент before будет иметь определённый стиль. Безусловно, он также будет содержать сообщение пользователю, что его браузер не поддерживает псевдокласс :has().

Впрочем, на текущий момент псевдоселектор :has() имеет хорошую поддержку браузерами.