Перейти к содержанию

Группировка селекторов

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

  • Найти селекторы, у которых есть одинаковые свойства.
  • Выбрать базовый селектор.
  • Перечислить все найденные селекторы в объявлении базового селектора.
  • Все новые селекторы добавлять в список селекторов базового объявления.

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

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

.header,
.main,
.footer {
  background-color: #f5f5f5;
}

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

Окей, а что же тогда предлагают препроцессоры? — писать код и не обращать внимание на такие мелочи. Конечно, такое говорится с оговоркой на то, что изредка придётся использовать препроцессорный псевдокласс :extend().

Рассмотрим несколько тривиальных примеров для погружения в курс дела и получения первого опыта работы с :extend().

Примеры

Пример 2.4.1

Чтобы понять, как работает этот псевдокласс, представим, что у нас есть три селектора и свойства у них одинаковые.

.class-1 {
  background-color: #fff;
  color: #000;
}

.class-2 {
  background-color: #fff;
  color: #000;
}

.class-3 {
  background-color: #fff;
  color: #000;
}

В этом случае можно полностью объединить .class-1, .class-2 и .class-3. Если провести такие манипуляции с нашим примером, то получится следующий код:

.class-1,
.class-2,
.class-3 {
  background-color: #fff;
  color: #000;
}

Less предлагает эквивалентное решение, но в тоже время более простое. Он говорит нам, что нужно объявить полностью лишь селектор .class-1, а другие сгруппировать с ним. Для этого предлагается использовать :extend().

.class-1 {
  background-color: #fff;
  color: #000;
}

.class-2:extend(.class-1) {
}

.class-3 {
  &:extend(.class-1);
}

Важно понимать, что использовать :extend() можно как и любой другой псевдокласс. Имеется в виду, что допустимо как прямое написание (.class:extend()), так и с использованием родительского селектора (.class { &:extend() }).

Проведя процедуру компиляции на выходе мы получим все тот же CSS, который и получили при ручной оптимизации:

.class-1,
.class-2,
.class-3 {
  background-color: #fff;
  color: #000;
}

Пример 2.4.2

Можно указывать как один селектор для расширения, так и несколько.

.class-1 {
  background-color: #fff;
}
.class-2 {
  background-color: #fff;
}

.selector {
  &:extend(.class-1, .class-2);
}

В результате такого объявления класс .selector будет группироваться и с первым (.class-1) селектором, и со вторым (.class-2).

.class-1,
.selector {
  background-color: #fff;
}
.class-2,
.selector {
  background-color: #fff;
}

Расширение правил

Селекторы можно не только группировать, но и расширять. Все эти громкие слова на самом деле пляшут вокруг все того же :extend(). Ранее рассматривались примеры, в которых все свойства у селекторов были одинаковыми и это те самые идеальные случаи. На практике такое встречается редко, но все таки встречается. Чаще всего попадаются другие случаи, когда часть свойств совпадают, а часть — нет. В этом случае опять пригодится этот же псевдокласс.

Пример 2.4.3

В студии «Артемка и КО» разрабатывается дизайн для продающего лендинга известной компании, производящей конфеты.

.global-header {
  background-color: #fff;
  color: #000;
}

.global-navigation {
  border: 1px solid #ddd;
}

По дизайну, селектору .global-navigation полагается белый фон и чёрный цвет у текста. Можно было бы просто скопировать недостающие свойства, но по желанию верстальщика решили использовать :extend(), тем самым получив недостающие свойства.

.global-header {
  background-color: #fff;
  color: #000;
}

.global-navigation {
  &:extend(.global-header);
  border: 1px solid #ddd;
}

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

Группировка с цепочкой селекторов

Препроцессоры настолько умные, что в состоянии уследить за цепочкой селекторов с одним именем. Делается это с помощью все того же псевдокласса :extend(), но с добавлением некоего параметра all.

Пример 2.4.4

Студия «Артемка» получила свои первые правки по макету для компании, производящей конфеты. Заказчик хочет, чтобы подвал сайта имел такие же стили, как и шапка сайта.

.global-header {
  background-color: #fff;

  .area {
    text-align: center;
  }
}

.global-header:hover {
  background-color: #000;
}

Верстальщик вспоминает о возможности группировки селекторов .global-footer со всеми селекторами, имеющими имя .global-header. На ум ему приходит только одно:

.global-footer {
  &:extend(.global-header);
}

Однако, после компиляции он получает не совсем желанный результат. Селектор .global-footer группируется лишь с самым первым селектором, а остальные игнорируются.

.global-header,
.global-footer {
  background-color: #fff;
}
.global-header .area {
  text-align: center;
}
.global-header:hover {
  background-color: #000;
}

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

.global-footer {
  &:extend(.global-header all);
}

В итоге получается требуемый результат. Студия «Артемка» продолжает работу над проектом, а заказчик продолжает присылать желанные правки.

.global-header,
.global-footer {
  background-color: #fff;
}
.global-header .area,
.global-footer .area {
  text-align: center;
}
.global-header:hover,
.global-footer:hover {
  background-color: #000;
}

Контекстная группировка

Less поддерживает группировку с конкретным селектором. Речь идёт про то, что :extend() может принимать в качестве цели не только название селектора, но и селектор с необходимой вложенностью, контекстом и некоторыми другими параметрами.

Псевдокласс :extend() умеет работать с:

  • nth: :extend(:nth-child(n+3)).
  • атрибутами: :extend([title=identifier]).
  • псевдоклассами: :extend(link:visited:hover).
  • псевдоэлементами: :extend(link:before).

Пример 2.4.5

Верстальщик получает макет, в котором стили оформления товаров в каталоге и статей в блоге имеют общие стили, но лишь в шапке.

.item {
  background-color: #fff;
  border: 1px solid #ddd;

  .header {
    padding: 25px;
  }
}

Ему хочется унаследовать стили .item .header, но не хочется городить лишнего кода. Приходится использовать контекстную группировку с применением уже знакомого нам псевдокласса :extend().

.item {
  background-color: #fff;
  border: 1px solid #ddd;

  .header {
    padding: 25px;
  }
}

.article {
  &:extend(.item .header);
}

После компиляции получается код, который полностью соответствует ожиданиям верстальщика и его требованиям:

.item {
  background-color: #fff;
  border: 1px solid #ddd;
}
.item .header,
.article {
  padding: 25px;
}

Мысли и советы

Пожалуй, это самый объёмный по материалу функционал для изучения, предоставляемый CSS-препроцессором Less. В то же время, это наименее используемый функционал в реальных проектах, так как приходится запоминать имена селекторов для группировки, а при достаточно объёмном проекте это не позволительная роскошь.

Я бы советовал аккуратно работать с :extend() и использовать его лишь тогда, когда это действительно оправданный шаг. Благодаря известному всем разработчикам фреймворку Bootstrap, я стал использовать :extend() лишь для группировки селекторов с селектором .clearfix. Такой подход позволяет исключить появление дополнительных классов в документе, тем самым делая структуру более чистой.