Понять комбинаторные селекторы и селекторы потомков в CSS

Kate

Administrator
Команда форума
Периодически я сталкиваюсь с проблемой - не хочу ставить лишний класс элементу внутри различных кнопок, ссылок и label-ов. Зачастую это текст, который нужно подсветить или галочка чекбокса или маленькая иконка.
Как же справиться с такой проблемой? Тут самым простым и понятным решением будет наследование. Возьмём код ссылки с иконкой, которую нужно анимировать:
<a href="#" class="text_arrow-btn">
<span>Все товары</span>
<svg width="37" height="15" viewBox="0 0 37 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 7.5H36M36 7.5L31.1538 0.5M36 7.5L31.1538 14.5" stroke="#323F4C"></path>
</svg>
</a>
Сделать это несложно, достаточно просто прописать несколько правил:
/*БАЗОВЫЕ ПРАВИЛА*/
a {
text-decoration: none;
color: inherit;
font-family: sans-serif;
}

.text_arrow-btn {
display: flex;
align-items: center;
font-size: 22px;
line-height: 142%;
}

/*ИСПОЛЬЗОВАНИЕ НАСЛЕДОВАНИЯ*/
.text_arrow-btn span { /*Для span которые внутри ссылки*/
margin-right: 16px;
}

.text_arrow-btn svg { /*Для svg которые внутри ссылки*/
transition: transform 0.3s;
width: 36px;
height: 14px;
}

.text_arrow-btn:hover span {
text-decoration: underline;
}

.text_arrow-btn:hover svg {
transform: translateX(12px);
}
Пробел обращается ко всем детям элемента с подходящим селектором. Символ "больше" > обратится только к прямому потомку.
Да, в CSS выглядет не очень, но в SASS всё уже немного приятнее:
a
text-decoration: none
color: inherit
font-family: sans-serif
color: #323F4C


.text_arrow-btn
display: flex
align-items: center
span
margin-right: 16px
svg
transition: transform 0.3s
width: 36px
height: 14px
path
transition: stroke 0.3s
&:hover
svg
transform: translateX(12px)
span
text-decoration: underline
&:active
color: #B6B5B5
path
stroke: #B6B5B5
Думаю что этим я глаза никому не открыл - вполне стандартная история.

Соседний комбинатор - "+"​

Однако, на этом мы не останавливаемся. Теперь другая задача - стилизация чекбокса. Если вы хоть раз сталкивались с этой проблемой, то вы знаете лицо боли. Потому что убрать стандартные стили просто так не получится, получится только убрать чекбокс к чёртовой бабушке и стилизовать заново из div или span. Но чтобы добавить неинтерактивным элементам интерактивности придётся использовать JS. Или не придётся?..
Тут нам поможет волшебный + - это комбинатор родственных элементов. Он объединяет два элемента, находящихся на одном уровне, и второй должен следовать СРАЗУ за первым.
Звучит сложно - работает просто. Выбирается следующий сразу за элементом селектор. Вот так:
<label class="main-checkbox">
<input type="checkbox">
<span>
<svg width="13" height="10" viewBox="0 0 13 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 3.85714L5.30435 9L12 1" stroke="#323F4C"></path>
</svg>
</span>
</label>
.main-checkbox
cursor: pointer
span
display: inline-block
width: 24px
height: 24px
background: #F8F8F8
display: flex
align-items: center
justify-content: center
transition: background 0.3s
svg
width: 12px
height: 9px
opacity: 0
transition: opacity 0.3s
&:hover
span
background: #EBEBEB
input
display: none
&:checked+span /*вот он*/
background: #F8F8F8
svg
opacity: 1
Обращаем внимание на: &:checked+span тут "+" говорит нам о том, что будет выбран следующий за чекнутым input span. И вуаля - чекбокс работает как часы и он стилизован.

Родственный комбинатор - "~"​

Но мы не останавливаемся. Имеем ещё одну задачу: чекбокс у которого внутри идёт span и svg одновременно и нужно при наведении изменить цвет и текста и svg. Да, можно обернуть элементы в div и использовать +, но фу, лишний тег - это полный отстой.
Тут нам поможет тильда ~ (не путать с тильтом). Этот символ не выражает усталость и безысходность - совсем наоборот. Он даёт нам выбрать теги, находящиеся на одном уровне с элементом.
Вот наш чекбокс с иконкой:
<label class="radio-sort">
<input type="checkbox">
<span>По возрастанию цены</span>
<svg width="11" height="14" viewBox="0 0 11 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.5 14V1M5.5 1L10 5.78947M5.5 1L1 5.78947" stroke="#323F4C" />
</svg>
</label>
И вот стили:
.radio-sort
font-family: sans-serif
margin-right: 48px
cursor: pointer
display: flex
align-items: center
&:last-child
margin-right: 0
input
display: none
span
display: inline-block
svg
width: 10px
height: 14px
display: inline-block
margin-left: 12px
input:checked + span
font-weight: 800
input:hover + span
color: #1eaf9f
input:hover ~ svg
path
stroke: #1eaf9f
тильда выберет следующий за span svg - очень удобно!
Итак, подведём итог.
Пробел - выберет все дочерние элементы, соответствующие селектору:
d7209b0a188743c9307163a3fc8c776e.png

Знак больше > выберет только дочерние элементы, являющиеся прямыми потомками:
02040fd515973303ed3594dc7f96cd3a.png

Знак + выберет следующего за элементом потомка:
ac39cc0926b0465c6e4110aea44af803.png

Тильда ~ выберет всех одноуровневых потомков:
afa044e753c4ac04ed90f26adb274520.png



 
Сверху