Когда нужно выбрать элемент не сам по себе, а по его положению относительно другого - например, абзац сразу внутри блока или заголовок, за которым следует текст, - на помощь приходят комбинаторы. Это специальные знаки между двумя селекторами: пробел, >, + и ~ - каждый описывает свою связь между элементами.
В этом уроке
Попробуй прямо в этом окне: все три абзаца становятся оранжевыми (это работает "article p" - потомок на любой глубине). Жирными и с голубым фоном становятся только первый и третий абзацы - они прямые дети article и настоящие соседи заголовка. А вот подчёркивание получает только самый первый абзац - только он идёт сразу за h2 без ничего между ними. Абзац внутри div остаётся без жирности, подчёркивания и фона: у него другой родитель - div, а не article, так что для селекторов "дочерний" и "сосед" он вообще не в счёт.
Селектор потомка (пробел)
Если между двумя селекторами стоит просто пробел, это селектор потомка: он находит второй элемент внутри первого на любой глубине вложенности, даже если между ними ещё десять других тегов.
article p { color: tomato; } - покрасит любой абзац внутри article, даже если он спрятан ещё в одном div.
article, p { color: tomato; } - запятая вместо пробела превращает это в два независимых селектора (все article И все p на странице), а не в связь "абзац внутри article".
Дочерний селектор (>)
Знак > между селекторами сужает поиск: он находит только прямых детей первого элемента, то есть тех, кто вложен ровно на один уровень, а не спрятан ещё глубже.
article > p { font-weight: bold; } - только абзацы, которые лежат прямо внутри article, а не внутри вложенного div.
article > div > p { } путают с article p - первое требует именно такой цепочки вложенности (article, в нём сразу div, в нём сразу p), а не просто "абзац где-то внутри article".
Селектор соседнего элемента (+)
Знак + связывает двух соседей с общим родителем: он находит элемент, который идёт СРАЗУ после первого, без других тегов между ними. Обычный текст и HTML-комментарии между ними не мешают - в счёт идут только другие теги.
h2 + p { text-decoration: underline; } - подчеркнёт абзац, только если он идёт первым сразу после h2.
h2 + p, ожидая, что сработает и для второго, и для третьего абзаца после заголовка - + находит только САМОГО БЛИЖАЙШЕГО соседа, а не всех подряд.
Селектор всех последующих соседей (~)
Знак ~ - более щедрая версия +: он находит ВСЕХ последующих соседей первого элемента с тем же родителем, а не только самого ближнего. Между ними может быть сколько угодно других тегов.
h2 ~ p { background: #eef; } - подсветит все абзацы, которые идут после заголовка и делят с ним одного родителя, сколько бы их ни было.
h2 ~ p, ожидая, что подсветится и абзац, спрятанный внутри вложенного div, - для ~ важно, чтобы элементы были настоящими соседями с ОДНИМ родителем, а не просто располагались где-то после заголовка по тексту разметки.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Путать пробел (потомок) и > (дочерний) | Пробел находит элемент на любой глубине вложенности, а > - только прямых детей. С пробелом правило часто задевает больше элементов, чем задумывалось | Использовать >, если важно ограничиться именно прямыми детьми, а не любой вложенностью |
| Путать + и ~ | + находит только самого ближайшего следующего соседа, а ~ - вообще всех последующих. Если между элементами появится ещё один тег, + перестанет работать, а ~ - нет | Использовать + для "прямо следующий элемент", ~ - для "любой из последующих" |
| Считать, что элемент внутри вложенного тега - тоже "сосед" | И + , и ~ требуют, чтобы элементы были настоящими соседями с ОДНИМ родителем. Элемент, спрятанный в другом теге между ними, соседом не считается, даже если по тексту разметки идёт между ними | Проверять реальную структуру вложенности, а не только порядок тегов на глаз |
| Путать комбинатор с составным селектором | Комбинатор всегда стоит МЕЖДУ двумя разными селекторами (a b, a > b) и связывает разные элементы. Слитная запись без пробела (a.klass) - это один и тот же элемент сразу с двумя условиями, а не комбинатор | Смотреть, есть ли пробел или знак между частями селектора - это и есть комбинатор; без пробела это одно условие на один элемент |
Шпаргалка
| Комбинатор | Что выбирает |
|---|---|
| a b (пробел) | Потомок - b внутри a на любой глубине |
| a > b | Дочерний - b, который прямой ребёнок a |
| a + b | Соседний - b, который идёт сразу после a, с тем же родителем |
| a ~ b | Все последующие соседи - любой b после a с тем же родителем |
| Специфичность | Сами комбинаторы вес селектора не добавляют - считаются только теги, классы и id по обе стороны от них |
Коротко: отвечаем на вопросы
В чём разница между div a {} и div > a {}?
div a найдёт ссылку на любой глубине внутри div, даже если она обёрнута ещё одним тегом. div > a сработает только для ссылки, которая лежит прямо внутри div, без промежуточных тегов вокруг неё.
Можно ли использовать несколько комбинаторов в одном селекторе подряд?
Да, их можно комбинировать сколько угодно: например, article > h2 + p сначала найдёт h2, который прямой потомок article, а затем - абзац, идущий сразу за этим h2.
Влияют ли комбинаторы на специфичность селектора?
Сами по себе нет - пробел, >, + и ~ веса не добавляют. На специфичность влияют только теги, классы и id, которые стоят по обе стороны от комбинатора.
Почему h2 + p ничего не подсветил, хотя абзац идёт сразу после заголовка на вид?
Скорее всего, между ними на самом деле есть ещё один тег - например, обёртка div. Обычный текст и HTML-комментарии на это не влияют, а вот любой другой элемент между h2 и p разрывает соседство для +.