:has
Сложные селекторы в CSS, особенно :has() и :contains() (которого нет в CSS), часто путают.
✅ 1. :has() — есть в CSS, работает в новых браузерах
Позволяет выбрать родителя, если внутри есть определённый элемент
📌 Пример:
css CopyEdit div:has(.carousel-item) {
border: 1px solid red;
}
Что делает:
Выделит все div, в которых есть элемент с классом .carousel-item.
🎯 Пример твоей задачи: Найти контейнер-карусель
Допустим, HTML такой:
<div>
<div class="carousel-item">1. Pick Your Artwork</div>
<div class="carousel-item">2. Pick Your Size</div>
<div class="carousel-item">3. Install Easily</div>
</div>
Ты хочешь применить стили к родителю этих .carousel-item, даже если он не имеет никакого класса:
div:has(> .carousel-item) {
background-color: #f0f0f0;
}
>— ищет только прямых детей- Без
>— найдёт любого потомка
🚫 2. :contains() — НЕ существует в CSS
Это был старый CSS3 draft, но так и не вошёл в стандарт.
❌ Не работает:
div: contains("1. Pick Your Artwork"); /* НЕ РАБОТАЕТ */
💡 Альтернатива: использовать :has() с селектором и обработать структуру.
Если ты не можешь ориентироваться по классу, но можешь по тексту — тогда CSS уже не поможет, и нужно использовать JavaScript или jQuery.
✅ JavaScript альтернатива по тексту
document.querySelectorAll("div").forEach((div) => {
if (div.textContent.includes("1. Pick Your Artwork")) {
const parent = div.closest("div"); // получаем контейнер
parent.style.background = "lightyellow";
}
});
💡 Общие примеры :has()
| CSS-селектор | Что делает |
|---|---|
section:has(h2) |
Все section, внутри которых есть h2 |
div:has(.error-message) |
Все div, у которых внутри есть элемент с классом |
ul:has(li.selected) |
Все ul, содержащие активный элемент |
.card:has(img[src*="logo"]) |
Все .card, где изображение — это логотип |
🧪 Поддержка :has() (на 2025 год):
| Браузер | Поддержка |
|---|---|
| Chrome 105+ | ✅ |
| Edge 105+ | ✅ |
| Safari 15.4+ | ✅ |
| Firefox | ❌ (в процессе разработки) |
🧠 Firefox пока не поддерживает :has(), так что в критичных случаях — fallback или JS.
А можно усложнить и попробовать найти родителя у которого именно 3 ребёнка .carousel-item?
Да, можно! В CSS селекторе :has() пока нет встроенной поддержки подсчёта количества элементов, типа :has(> .carousel-item:count(3)) — такого не существует.
Но есть обходной путь с помощью селекторов :nth-child() и комбинации :not().
✅ Пример: выделить div, у которого ровно 3 .carousel-item
Вот такой HTML:
<div class="carousel">
<div class="carousel-item">1</div>
<div class="carousel-item">2</div>
<div class="carousel-item">3</div>
</div>
🎯 CSS селектор (примерно имитирующий “3 .carousel-item”):
div:has(
> .carousel-item:nth-child(1):not(:nth-child(4)),
> .carousel-item:nth-child(2),
> .carousel-item:nth-child(3)
):not(:has(> .carousel-item:nth-child(4))) {
border: 2px solid red;
}
Что делает:
- Удостоверяется, что есть
.carousel-itemна позициях 1, 2, 3 - И нет
.carousel-itemна позиции 4 - Тем самым условно проверяет, что
.carousel-itemровно 3
⚠️ Ограничения:
- Это жёстко завязано на порядок элементов
- Не работает, если среди детей есть другие, не
.carousel-item
🧠 Надёжный способ — через JavaScript
Если тебе действительно нужно условие “ровно три”, JS даст куда больше контроля:
document.querySelectorAll("div").forEach((div) => {
const items = div.querySelectorAll(":scope > .carousel-item");
if (items.length === 3) {
div.style.border = "2px solid green";
}
});
✅ Что делает:
:scope > .carousel-item— только прямые дети- Если таких ровно три, тогда стили применяются
Вот этот фрагмент:
div:has(
> .carousel-item:nth-child(1):not(:nth-child(4)),
> .carousel-item:nth-child(2),
> .carousel-item:nth-child(3)
):not(:has(> .carousel-item:nth-child(4))) {
border: 2px solid red;
}
Можно упростить до:
div:has(
> .carousel-item:nth-child(1),
> .carousel-item:nth-child(2),
> .carousel-item:nth-child(3)
):not(:has(> .carousel-item:nth-child(4))) {
border: 2px solid red;
}
Это работает, если:
.carousel-itemстоят подряд с начала- И нет четвёртого элемента
Но почему в некоторых примерах добавляют :not(:nth-child(4))?
Это делают:
- Для страховки, если селектор должен быть строго детерминирован по позиции
- Либо по ошибке — избыточная защита от четвертого, которая уже есть во второй части селектора
Вывод:
✅ Да, :not(:has(...nth-child(4))) уже исключает лишние элементы.
❌ :not(:nth-child(4)) у :nth-child(1) избыточен в этом контексте.