Родительские селекторы
Чтение — около 7 минут· без учёта практики
В этой статье мы с вами расширим наши знания об использовании различных CSS селекторов в LESS, научимся
работать с псевдоэлементами, и рассмотрим использование специального оператора &, который
представляет из себя ссылку на полный родительский селектор внутри вложенного правила. Подробную информацию о правилах
вложенности вы можете получить в статье этого учебника «Правила вложенности».
До того как вы перейдете к прочтению статьи, рекомендую вам освежить, или получить знания из актуального учебника CSS о том как, и для чего используются селекторы, псевдоклассы и псевдоэлементы при описании стилей:
- Селекторы. Часть 1
- Селекторы. Часть 2
- Псевдоклассы и псевдоэлементы
- Селекторы. Часть 3
- Селекторы. Часть 4
Использование псевдоклассов
Псевдоклассы дают нам возможность затронуть форматирование на странице такие особенности, или состояния элементов,
которые явным образом не задать с использованием селекторов. В этом разделе статьи мы с вами научимся правильно
использовать псевдоклассы в LESS коде и начнем свое знакомство с оператором &, его так же
называют оператор родительского селектора (внутри вложенного правила).
Давайте начнем наше знакомство со следующего примера:
<html>
<head>
<title>Пример работы с псевдоклассами в LESS</title>
<link rel="stylesheet" href="12.css">
</head>
<body>
<button>Клик</button>
</body>
</html>
В примере одна кнопка. Исходник 12.less сначала компилируем командой
npx lessc 12.less 12.css, а в HTML подключаем 12.css.
button {
height: 50px; // высота элемента
width: 100px; // ширина элемента
padding: 15px 30px; // внутренние отступы
border: 1px solid transparent; // сплошная прозрачная граница размером 1 пиксель (убираем встроенные стили)
border-radius: 15px; // форма границ элемента
background-image: linear-gradient(to right, #f31, #f64); // линейный градиент в качестве фонового изображения
color: #fff; // цвет текста
&:hover { // задаем стили при наведении на элемент button
color: #000; // цвет текста
}
&:active { // задаем стили тогда, когда элемент button активен
background: orange; // цвет заднего фона
}
}
Оператор & подставляет полный родительский селектор, а не ищет родителя в DOM. Например, внутри
.toolbar button запись &:hover даст .toolbar button:hover. Без
& компилятор добавит пробел: это другой корректный селектор, который выбирает потомка в состоянии
:hover.
Обратите внимание на синтаксис, который мы использовали при описании стилей, в нем в отличии от CSS вместо
имени родительского элемента, в нашем случае это button мы просто используем оператор родительского селектора
&. В результате компиляции у вас должны получиться следующие CSS стили:
button {
height: 50px; /* высота элемента */
width: 100px; /* ширина элемента */
padding: 15px 30px; /* внутренние отступы */
border: 1px solid transparent; /* сплошная прозрачная граница размером 1 пиксель (убираем встроенные стили) */
border-radius: 15px; /* форма границ элемента */
background-image: linear-gradient(to right, #f31, #f64); /* линейный градиент в качестве фонового изображения */
color: #fff; /* цвет текста */
}
button:hover { /* задаем стили при наведении на элемент button */
color: #000; /* цвет текста */
}
button:active { /* задаем стили тогда, когда элемент button активен */
background: orange; /* цвет заднего фона */
}
В этом примере мы использовали два псевдокласса, которые позволили нам описать стили для тех состояний, когда
указатель мыши наведен на элемент
<button>
(псевдокласс
:hover) и, когда он активен (псевдокласс
:active).
Проверьте полученный CSS
Это результат компиляции, а не редактор LESS. Измените button:hover и проверьте наведение; используйте Tab, чтобы проверить видимый фокус.
index.html
<html>
<head>
<meta charset="UTF-8" />
<title>Родительский селектор LESS</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<button>Клик</button>
</body>
</html>
style.css
button {
height: 50px;
width: 100px;
padding: 15px 30px;
border: 1px solid transparent;
border-radius: 15px;
background-image: linear-gradient(to right, #f31, #f64);
color: #fff;
}
button:hover {
color: #000;
}
button:active {
background: orange;
}
button:focus-visible {
outline: 3px solid #245b78;
outline-offset: 3px;
}
Использование методологии БЭМ
Применение оператора родительского элемента можно встретить не только вместе с псевдоклассами, но зачастую и при написании CSS кода по такой методологии веб-разработки как БЭМ (Блок-Элемент-Модификатор). Обязательно ознакомьтесь с ней в свободное время, она предлагает общую семантическую модель для всех технологий, использующихся во фронтэнд разработке (HTML, CSS, JavaScript, шаблоны проектирования и так далее).
Обратите внимание на следующее изображение:
На изображении представлены два идентичных блока, за тем исключением, что к элементу первого блока применен модификатор, который изменяет цвет текста, а у второго блока использован модификатор, который увеличивает размер шрифта в два раза.
Сделаем по этому изображению разметку (у многих редакторов кода по умолчанию встроен, или доступен для скачивания удобный плагин для быстрой разметки Emmet, он позволяет сделать подобную разметку следующим образом: (div>div*3)*2 и Tab). Результат:
<div> <!-- первый блок -->
<div></div> <!-- элемент первого блока -->
<div></div> <!-- элемент первого блока -->
<div></div> <!-- элемент первого блока -->
</div>
<div> <!-- второй блок -->
<div></div> <!-- элемент второго блока -->
<div></div> <!-- элемент второго блока -->
<div></div> <!-- элемент второго блока -->
</div>У вас может быть на странице сотни различных (не идентичных) блоков, и при этом правила одного блока не должны влиять на внутренний мир любого другого блока (компонентный подход). Блок должен влиять только на свои элементы, и не может воздействовать на элементы другого блока, элементы блока это ничто иное как его внутренняя реализация. Кроме того, блоки могут быть вложены в другие блоки, но это уже совсем другая история.
Модификаторы в свою очередь задают блокам внешний вид, состояние и поведение, то есть изменение оформления блока производится только при помощи установки, либо снятия модификатора. Еще одним важным моментом является то, что в БЭМ не используют селекторы типа (элементов) и селекторы идентификаторов, а стили блоков и элементов описываются с использованием селекторов классов.
Давайте опишем наши блоки с использованием LESS:
.block { // блок
font-family: monospace; // семейство шрифтов
&__element { // элемент блока
padding: 10px; // внутренние отступы
margin: 30px; // внешние отступы
color: red; // цвет текста
&_modifier_active { // модификатор элемента
color: green; // цвет текста
}
}
&_modifier_s { // модификатор блока
font-size: 15px; // размер шрифта
}
&_modifier_m { // модификатор блока
font-size: 30px; // размер шрифта
}
}Обратите внимание на отличие в наименование блоков, как правило их пишут либо в одно слово, либо через дефис, элементы выделяются двумя нижними подчеркиваниями, а модификаторы одним. В результате компиляции у вас должен получиться следующий CSS код:
.block { /* блок */
font-family: monospace; /* семейство шрифтов */
}
.block__element { /* элемент блока */
padding: 10px; /* внутренние отступы */
margin: 30px; /* внешние отступы */
color: red; /* цвет текста */
}
.block__element_modifier_active { /* модификатор блока */
color: green; /* цвет текста */
}
.block_modifier_s { /* модификатор блока */
font-size: 15px; /* размер шрифта */
}
.block_modifier_m { /* модификатор блока */
font-size: 30px; /* размер шрифта */
}Представленная статья ориентирована прежде всего на изучение LESS и оператора родительского элемента в частности, если вам на данном этапе не до конца понятна суть методологии БЭМ не растраивайтесь, так как она требует самостоятельного более детального изучения, и не является обязательной на данном этапе обучения.
Конечная разметка документа должна принять следующий вид:
<div class="block block_modifier_s"> <!-- первый блок с модификатором -->
<div class="block__element block__element_modifier_active"></div>
<div class="block__element"></div>
<div class="block__element"></div>
</div>
<div class="block block_modifier_m"> <!-- второй блок с модификатором -->
<div class="block__element"></div>
<div class="block__element"></div>
<div class="block__element"></div>
</div>Нюансы использования &
Сравним состояние самого элемента и его потомка. Обе записи ниже компилируются в корректный CSS, но выбирают разные элементы.
// без оператора &
.test {
:hover { // используем псевдокласс :hover
color: green;
}
}
// с оператором &
.test2 {
&:hover { // используем псевдокласс :hover с оператором родительского элемента
color: green;
}
}
В первом случае .test :hover выбирает потомка элемента .test, на который навели указатель.
Во втором .test2:hover выбирает сам элемент .test2. Пробел меняет смысл, но не делает CSS
невалидным.
.test :hover { /* валидный CSS: состояние потомка */
color: green;
}
.test2:hover { /* валидный CSS код */
color: green;
}
Далее, где не обойтись без использования оператора &, это необходимость использования селектора, в
котором родительский элемент имеет составной селектор без пробелов (например, .parentClass.class):
// без оператора &
.test1 {
.class { // селектор потомков без оператора
color: blue;
}
// отсутствует возможность указать родительский элемент с определенным классом
}
// с оператором &
.test2 {
& .class { // селектор потомков с оператором
color: blue;
}
&.class { // селектор класса с определенным классом
color: plum;
}
}Результат компиляции будет следующий:
.test1 .class { /* селектор потомков */
color: blue;
}
.test2 .class { /* селектор потомков */
color: blue;
}
.test2.class { /* селектор класса с определенным классом */
color: plum;
}В следующем примере мы рассмотрим такие селекторы, в которых без использования оператора родительского элемента можно обойтись. Как и в случае с селекторами потомков компиляция даст валидный CSS код:
// без оператора &
.test1 {
> div { // селектор дочерних элементов
color: red;
}
~ a { // селектор следующих элементов
color: yellow;
}
+ p { // селектор смежных элементов
color: plum;
}
}
// с оператором &
.test2 {
& > div { // селектор дочерних элементов
color: red;
}
& ~ a { // селектор следующих элементов
color: yellow;
}
& + p { // селектор смежных элементов
color: plum;
}
}Как вы можете заметить при использовании селекторов потомков, дочерних элементов, следующих элементов и смежных элементов наличие оператора родительского элемента (для ссылки на родителя) будет носить лишь декоративный характер, так как компиляция даст валидный (одинаковый) результат в обоих случаях:
.test1 > div { /* селектор дочерних элементов */
color: red;
}
.test1 ~ a { /* селектор следующих элементов */
color: yellow;
}
.test1 + p { /* селектор смежных элементов */
color: plum;
}
.test2 > div { /* селектор дочерних элементов */
color: red;
}
.test2 ~ a { /* селектор следующих элементов */
color: yellow;
}
.test2 + p { /* селектор смежных элементов */
color: plum;
}Множественное использование &
Следующая особенность оператора родительского элемета, это возможность использования его внутри вложенного селектора неограниченное количество раз. Это позволит вам повторно использовать имя родительского элемента при составлении необходимого селектора. Рассмотрим следующий пример:
.block {
& > & { // селектор дочерних элементов
color: red;
}
& & { // селектор потомков
color: green;
}
&& { // селектор класса с определенным классом
color: yellow;
}
&, &-s, &-l { // групповой селектор
color: blue;
}
}
В этом примере мы составили селектор
дочерних элементов,
селектор потомков,
селектор класса
с классом родительского элемента и
групповой селектор, используя в основном только оператор &.
Результат компиляции будет следующий:
.block > .block { /* селектор дочерних элементов */
color: red;
}
.block .block { /* селектор потомков */
color: green;
}
.block.block { /* селектор класса с определенным классом */
color: yellow;
}
.block, /* групповой селектор */
.block-s,
.block-l {
color: blue;
}Изменение последовательности селекторов
Хочу познакомить вас еще с одной возможностью оператора родительского элемента, которая позволяет изменить последовательность селекторов, и изменить порядок частей итогового селектора. В некоторых случаях полезно внести одну правку, а не переписывать большое количество кода, но без особой необходимости старайтесь избегать подобных стилей. Рассмотрим следующий пример:
.parent {
.child {
.grandchild & { // меняем порядок частей селектора
color: green;
a {
background: blue;
}
}
.grandchild& { // меняем порядок частей селектора
color: green;
a {
background: yellow;
}
}
}
}
Обратите внимание, что мы использовали оператор родительского селектора не до, а уже после селектора, в
первом случае, чтобы у нас получился
селектор потомков, а во втором
селектор класса
с определенным классом. Обратите внимание, что вложенный в него элемент
<a>
не вырывается из контекста, а остается на том же уровне вложенности.
Результат компиляции будет следующий:
.grandchild .parent .child {
color: green;
}
.grandchild .parent .child a {
background: blue;
}
.grandchild.parent .child {
color: green;
}
.grandchild.parent .child a {
background: yellow;
}Комбинирование селекторов
Заключительная особенность оператора &, которую мы рассмотрим в этой статье это возможность создания
групповых селекторов, которые получаются путем перебора всех перечисленных в родительском элементе селекторов. При проведении перебора
элементов создаются всевозможные комбинации отдельно по каждому элементу. Рассмотрим на следующем примере:
i, b, em { // групповой селектор
& > & {
color: blue;
}
}Будьте внимательны при использовании этой возможности LESS, это может как сократить ваш код, так и привести к нежелательным ошибкам. Результат компиляции будет следующий:
i > i, /* групповой селектор */
i > b,
i > em,
b > i,
b > b,
b > em,
em > i,
em > b,
em > em {
color: blue;
}Границы применения
В БЭМ-примере &__element склеивает имя класса при сборке LESS. Нативный CSS-селектор
& не является заменой для такого склеивания. При миграции выпишите .block__element явно.
У длинных списков родителей повторение & перемножает варианты: для двух родителей запись
& + & даёт четыре комбинации. Проверяйте размер и смысл скомпилированных селекторов.
Для кнопки дополнительно сохраняйте видимый фокус клавиатуры: не удаляйте outline без равноценной замены.
&:focus-visible компилируется по тому же принципу, что и &:hover.
Проверьте себя
Выберите ответ — объяснение появится сразу. Ответ можно менять.
Задание
Состояние самой кнопки. Исправьте вложенный селектор: цвет должен меняться у самой кнопки при наведении.
Добавьте через & состояние :focus-visible с контуром 2px solid #245b78.
Ожидаемый результат. Получаются .button:hover и .button:focus-visible без пробела
перед псевдоклассом. Клавиша Tab позволяет увидеть фокус кнопки.
Задание выполняется локально: скачайте архив практики,
распакуйте его и откройте папку parent-selectors. При первом запуске выполните npm ci в
корне распакованного архива. Встроенный редактор сайта не компилирует LESS.
styles.less
.button {
color: #245b78;
:hover { color: #16364a; }
}Из корня архива соберите выбранное задание:
npx lessc --math=parens-division parent-selectors/styles.less parent-selectors/styles.css
Сначала сравните селекторы и значения в полученном CSS с ожидаемым результатом. Для визуальной проверки откройте
index.html из той же папки. После правки повторите сборку и обновите страницу.
Показать решение
Это один из возможных вариантов. Сравните смысл своего CSS с результатом; пробелы и переносы строк несущественны.
styles.less
.button {
color: #245b78;
&:hover { color: #16364a; }
&:focus-visible { outline: 2px solid #245b78; }
}