Псевдокласс Extend
В этой статье мы с Вами рассмотрим псевдокласс LESS, который позволяет передать препроцессору работу, связанную с объединением (группировкой) селекторов.
Базовое применение и синтаксис
Extend это псевдокласс LESS, который позволяет объединить селектор в котором он был вызван, с тем, или теми селекторами на которые он ссылается при вызове. Грубо говоря, он позволяет создать групповой селектор, который объединит в себе стили необходимые для селектора в котором он был вызван, или для которого он был вызван с уже имеющимися селекторами. Перейдем к рассмотрению базового применения и синтаксиса псевдокласса extend. Допустим у нас объявлены следующие стили:
.myElement { color: red; border: 1px solid red; } .myAnotherElement { font-size: 2em; }
Представим, что нам необходимо добавить стили элемента .myElement элементу .myAnotherElement, LESS в этом случае предлагает нам использовать такой инструмент как псевдокласс extend:
.myElement { color: red; border: 1px solid red; } .myAnotherElement { &:extend(.myElement); // используем оператор родительского элемента и псевдокласс extend font-size: 2em; }
В этом примере мы использовали оператор родительского элемента, чтобы сослаться на родительский элемент и использовали псевдокласс extend, которому в качестве параметра передали имя класса, стили которого необходимо объединить с классом в котором мы использовали этот псевдокласс. Если Вы не знакомы с оператором родительского элемента, то вы можете ознакомиться с ним в статье этого учебника "Родительские селекторы".
Результат компиляции будет следующий:
.myElement, .myAnotherElement { color: red; border: 1px solid red; } .myAnotherElement { font-size: 2em; }
Еще один вариант, который бы привел к аналогичному результату компиляции приведен ниже:
.myElement { color: red; border: 1px solid red; } .myAnotherElement { font-size: 2em; } .myAnotherElement:extend(.myElement){} // используем псевдокласс extend
В этом случае мы не стали использовать оператор родительского элемента, а явно указали селектор, который мы хотим объединить с имеющимся селектором. Кроме того, во избежании ошибки компиляции мы указали блок объявления стиля {}, это необходимо по той причине, что мы используем псевдокласс extend не внутри блока объявления, как в предыдущем примере.
С первого взгляда преимущество использования псевдокласса extend может быть не очевидно, особенно на крупных проектах, но не торопитесь c выводами, возможно, он сможет решить конкретно вашу задачу.
Допускается вызывать псевдокласс extend не только по отношению к одному селектору, но и передавать несколько селекторов:
.myElement1 { color: red; } .myElement2 { border: 1px solid red; } .myAnotherElement { &:extend(.myElement1, .myElement2); // используем псевдокласс extend с несколькими селекторами font-size: 2em; }
В этом примере мы использовали оператор родительского элемента, чтобы сослаться на родительский элемент и использовали псевдокласс extend, которому в качестве параметра передали два имени класса, стили которых необходимо объединить с классом .myAnotherElement, в котором мы использовали этот псевдокласс.
Результат компиляции будет следующий:
.myElement1, .myAnotherElement { color: red; } .myElement2, .myAnotherElement { border: 1px solid red; } .myAnotherElement { font-size: 2em; }
Классическое использование extend
Давайте рассмотрим с Вами классическое использование псевдокласса extend, допустим у нас объявлены классы fruit и apple, которые имеют следующие стили:
.fruit { background: red; transition: 0.5; // определяем сколько времени занимает эффект перехода } .apple { background: green; }
Мы хотим разместить элемент, который бы получил цвет заднего фона объявленного в классе apple и получил оставшиеся стили класса fruit, для этого нам необходимо использовать оба класса и сделать следующую разметку документа:
<div class = "fruit apple"></div>
В этом случае происходит переопределение стилей и элемент получает зеленый цвет заднего фона по той причине, что класс apple при одинаковой значимости стилей размещен ниже в таблице стилей и поэтому имеет приоритет. Несмотря на то, что это решает нашу задачу, это может привести к ошибкам. Вы можете пойти другим путем и использовать псевдокласс extend для решения этой задачи:
.fruit { background: red; transition: 0.5; } .apple { &:extend(.fruit); // используем оператор родительского элемента и псевдокласс extend background: green; }
В этом примере мы использовали оператор родительского элемента, чтобы сослаться на родительский элемент и использовали псевдокласс extend, которому в качестве параметра передали имя класса fruit, стили которого необходимо объединить с классом apple, в котором мы использовали этот псевдокласс.
Результат компиляции будет следующий:
.fruit, .apple { background: red; transition: 0.5; } .apple { background: green; }
После этого нам достаточно разместить элемент только с классом apple для достижения поставленной перед нами задачи:
<div class = "apple"></div>
Использование extend с селекторами
Псевдокласс extend по аналогии с CSS псевдоклассами может использоваться вместе с селекторами:
.myElement1 { color: red; } .myElement2 { border: 1px solid red; } .a, .b :extend(.myElement1), // использование extend с селектором (синтаксис с использованием пробела) .c:extend(.myElement2) { // использование extend с селектором (синтаксис без использования пробела) background: yellow; }
В этом примере мы использовали псевдокласс extend для двух селекторов, в первом случае в качестве параметра псевдокласса передали имя класса .myElement1, а во втором .myElement2.
Обратите внимание на разницу в синтаксисе использования псевдокласса extend совместно с селекторами, допускается между селектором и псевдоклассом указывать пробел.
Результат компиляции будет следующий:
.myElement1, .b { color: red; } .myElement2, .c { border: 1px solid red; } .a, .b, .c { background: yellow; }
Селектор может содержать несколько псевдоклассов extend, но все они в обязательном порядке должны располагаться в конце селектора:
a:hover:extend(.myElement1):extend(.myElement2){} // компиляция успешна a:extend(.myElement1):hover:extend(.myElement2){} // ошибка компиляции
В этом, как правило, нет острой необходимости так как допускается перечислить необходимые селекторы в качестве параметров псевдокласса:
a:hover:extend(.myElement1, .myElement2){}
Extend внутри групповых селекторов
При размещении псевдокласса extend внутри группового селектора компилятор LESS создаст стили элементам таким образом, как будто мы разместили его отдельно в каждом селекторе этого набора правил, например:
.a{ color: red; } .b, .c, .d{ &:extend(.a); // размещение псевдокласса внутри группового селектора } .e:extend(.a), // размещение псевдокласса отдельно к каждому селектору .f:extend(.a), .g:extend(.a){ }
В этом примере мы разместили псевдокласс extend как внутри группового селектора, так и применили его отдельно к каждому селектору другого группового селектора. Обратите внимание, что при размещении внутри блока объявлений необходимо использовать оператор родительского элемента.
Как вы можете заметить ниже результат компиляции будет аналогичный:
.a, .b, .c, .d, .e, .f, .g{ color: red; }
Extend для вложенных селекторов
Допускается использование псевдокласса extend не только к родительским селекторам, но и к вложенным:
.a { .b { // вложенный селектор color: green; } } .error:extend(.b) {} // будет проигнорировано компилятором .ok:extend(.a .b) {} // будет скомпилировано
Обратите внимание, что необходимо указывать весь путь до вложенного селектора, иначе компилятор просто проигнорирует ваши стили. Подробнее об этом мы поговорим в следующем разделе. Результат компиляции будет следующий:
.a .b, .ok { color: green; }
Нюансы использования extend с селекторами
Важным нюансом в использовании псевдокласса extend является понимание того, что при его использовании компилятор ищет точное соответствие между селекторами, например, если мы ищем селектор класса, то он и должен быть селектором класса, а не селектор класса с определенным классом, или универсальный селектор, используемый с определенным классом. При этом порядок указания CSS псевдолассов тоже важен.
Давайте рассмотрим этот нюанс на следующем примере:
a:hover:active { background: green; } .error:extend(a:active:hover){} // будет проигнорировано компилятором .myClass > .someClass, .myClass.someClass, *.myClass { background: green; } .error:extend(.myClass){} // будет проигнорировано компилятором
Как вы можете догадаться компилятор LESS проигнорирует все объявления псевдокласса extend по той причине, что не найдет точного вхождения указанного селектора и результат компиляции останется неизменным:
a:hover:active { background: green; } .myClass > .someClass, .myClass.someClass, *.myClass { background: green; }
Следующий нюанс, который мы рассмотрим касается использования псевдокласса extend с CSS псевдоклассами, которые для поиска элементов в документе используют формулу. К таким CSS псевдоклассам относятся:
Мы уже с Вами говорили, что LESS при использованиии псевдокласса extend ищет точное вхождение селектора, это утверждение верно и при использовании его с вышеуказанными CSS псевдоклассами. Например, формулы 1n+5 и n+5 эквивалентны и приводят к поиску одних и тех же элементов, но для LESS они отличаются, его компилятор не умеет производить подобные вычисления и сравнивать математические формулы.
Рассмотрим следующий пример:
a:nth-child(1n+5) { color: green; } .error:extend( a:nth-child( n+5 ) ) {} // будет проигнорировано компилятором .ok:extend( a:nth-child( 1n+5 ) ) {} // будет скомпилировано
Результат компиляции будет следующий:
a:nth-child(1n+5), .ok { color: green; }
Следующей особенностью компилятора LESS является то, что он умеет сопоставлять селекторы как с одинарными, так и с двойными кавычками, так и без кавычек, для него они равнозначны, например:
[attr='title'] { background: green; } .ok:extend([attr="title"]){} .ok2:extend([attr=title]){} .error:extend([attr='title"]){} // ошибка компиляции
В этом примере мы объявили селектор атрибутов с указанным значением title, которое мы заключили в одинарные кавычки. В первом случае, мы вызываем псевдокласс extend с двойными кавычками, а во втором во все без кавычек. В обоих случаях компиляция будет успешна. Обратите внимание, что в третьем случае мы заключили значение атрибута с одной стороны в одинарные, а с другой в двойные кавычки, это приведет к ошибке компиляции. Результат компиляции первых двух примеров представлен ниже:
[attr='title'], .ok, .ok2 { background: green; }
Ключевое слово all
Псевдокласс extend поддерживает использование ключевого слова "all", оно сообщает компилятору LESS, что необходимо найти все вхождения селектора, или селекторов, переданных в качестве параметра псевдокласса, скопировать эти селекторы, заменяя при этом их имена на имя селектора на котором был вызван псевдокласс extend.
При работе с ключевым словом "all" используется следующий синтаксис:
// поиск и копирование происходит по одному селектору .replaceClass:extend(selector all ) {} // поиск и копирование происходит по нескольким селекторам .replaceClass:extend(selector, anotherSelector all ) {}
Давайте перейдем к рассмотрению примера:
.a { color: red; } .b { background: green; &:hover { // используем оператор родительского элемента color: green; } } .replaceClass:extend(.a, .b all ) {} // используем псевдокласс extend с ключевым словом all
В этом примере мы использовали псевдокласс extend, которому в качестве параметра мы передали два класса и использовали при этом ключевое слово "all", которое сообщает компилятору о необходимости копирования всех вхождений, переданных селекторов в класс replaceClass.
В результате компиляции мы получим следующие стили:
.a, .replaceClass { color: red; } .b, .replaceClass { background: green; } .b:hover, .replaceClass:hover { color: green; }
Интерполяция селекторов с extend
В этом разделе мы рассмотрим как происходит интерполяция селекторов совместно с использованием псевдокласса extend, если Вы не знакомы с понятием интерполяции, то вы можете изучить эту тему в статье этого учебника "Переменные в LESS".
Давайте рассмотрим следующий пример:
@myVar: .myClass; @{myVar} { // // интерполяция переменной внутри селектора display: flex; } .myClass2:extend( .myClass ) {} .myClass3:extend ( @{myVar} ) {} // интерполяция переменной в качестве параметра псевдокласса extend
Важной особенностью при работе с переменными является то, что компилятор LESS не способен сравнивать селекторы, переданные в качестве параметра псевдокласса extend с переменными. В нашем случае мы производим интерполяцию переменной @myVar внутри селектора. Кроме того, если в качестве параметра псевдокласса extend передать переменную, то компилятор просто проигнорирует ее.
Результат компиляции будет следующий:
.myClass { display: flex; }
Единственное место, где интерполяция переменных будет корректно работать с псевдоклассом extend это использование её вместо селектора, который мы хотим объединить с уже имеющимся селектором:
@variable: .myAnotherClass; .myClass { color: khaki; } @{variable}:extend( .myClass ) {}
Результат компиляции будет следующий:
.myClass, .myAnotherClass { color: khaki; }
Обнаружение дубликатов
Обратите внимание на еще одну особенность компилятора LESS при работе с псевдоклассами extend, в настоящее время, он не умеет находить дубликаты селекторов, что это значит, давайте рассмотрим на следующем примере:
.a, .b { color: red; } .c:extend( .a, .b ) {}
В этом примере у нас имеется групповой селектор, элементы которого мы хотим объединить с классом c. Для этого мы используем псевдокласс extend и передаем ему в качестве параметров эти классы. Результат компиляции может вас удивить:
.a, .b, .c, .c { // дубликат класса color: red; }
Не смотря на то, что это не повлияет на работоспособность вашего документа, это приведет к наличию лишнего мусора в вашем коде, запомните этот нюанс.
Использование extend с правилом @media
В настоящее время псевдокласс extend, объявленный внутри правила @media будет выбирать только те селекторы, которые находятся непосредственно внутри этого правила:
.selector { // extend проигнорирует этот селектор color: green; } @media print { .selector { // extend проигнорирует этот селектор color: red; } } @media screen { .selector { // extend выберет этот селектор color: blue; } .screenClass:extend(.selector) {} // вызываем псевдокласс extend внутри правила @media }
В этом примере мы вызвали псевдокласс extend внутри правила @media, в этом случае он выберет только тот селектор, который находится непосредственно внутри этого правила, а другие одноименные селекторы будут просто проигнорированы компилятором. Результат компиляции будет следующий:
.selector { color: green; } @media print { .selector { color: red; } } @media screen { .selector, .screenClass { color: blue; } }
Обратите внимание на то, что если внутри правила @media вложено другое правило, то в этом случае псевдокласс extend при вызове из родительского правила не сможет выбрать селекторы во вложенном правиле, например:
@media screen { .screenClass:extend( .myClass ) {} // вызываем псевдокласс extend внутри правила @media @media (max-width: 1200px) { .myClass { // extend проигнорирует этот селектор display: none; } } }
В этом примере мы вызвали псевдокласс extend внутри правила @media и передали в качестве параметра имя класса, размещенное во вложенном правиле, что было проигнорировано компилятором:
@media screen and (max-width: 1200px) { .myClass { display: none; } }
Мы с Вами рассмотрели использование псевдокласса extend внутри вложенных правил @media, давайте теперь рассмотрим его использование внутри глобальной области видимости на следующем примере:
@media screen { .myClass { // extend выберет этот селектор display: flex; } @media (max-width: 1200px) { .myClass { // extend выберет этот селектор display: none; } } } .global:extend(.myClass) {} // вызываем псевдокласс extend внутри глобальной области видимости
Если у Вас возникают трудности с пониманием областей видимости, то Вы можете вернуться к прочтению статьи учебника "Переменные в LESS". Псевдокласс extend, вызванный на верхнем уровне (в глобальной области видимости) позволяет выбрать все селекторы, включая селекторы находящиеся внутри вложенных правил.
Результат компиляции будет следующий:
@media screen { .myClass, .global { display: flex; } } @media screen and (max-width: 1200px) { .myClass, .global { display: none; } }