Псевдокласс 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
&:extend(.myElement);
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;
}
// используем псевдокласс extend
.myAnotherElement:extend(.myElement) {
}
В этом случае мы не стали использовать оператор родительского элемента, а явно указали селектор, который мы хотим объединить с имеющимся селектором. Кроме того, во избежании ошибки компиляции мы указали блок объявления стиля {}
, это необходимо по той причине, что мы используем псевдокласс 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;
}
// использование extend с селектором (синтаксис с использованием пробела)
.a,
.b :extend(.myElement1),
.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 псевдоклассам относятся:
:nth-child()
:nth-last-child()
:nth-of-type()
:nth-last-of-type()
Мы уже говорили, 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;
}
}
// используем псевдокласс extend с ключевым словом all
.replaceClass:extend(.a, .b 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) {
}
// интерполяция переменной в качестве параметра псевдокласса extend
.myClass3:extend ( @{myVar} ) {
}
Важной особенностью при работе с переменными является то, что компилятор 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 {
// вызываем псевдокласс extend внутри правила @media
.screenClass:extend(.myClass) {
}
@media (max-width: 1200px) {
// extend проигнорирует этот селектор
.myClass {
display: none;
}
}
}
В этом примере мы вызвали псевдокласс extend
внутри правила @media
и передали в качестве параметра имя класса, размещенное во вложенном правиле, что было проигнорировано компилятором:
@media screen and (max-width: 1200px) {
.myClass {
display: none;
}
}
Мы с Вами рассмотрели использование псевдокласса extend
внутри вложенных правил @media
, давайте теперь рассмотрим его использование внутри глобальной области видимости на следующем примере:
@media screen {
// extend выберет этот селектор
.myClass {
display: flex;
}
@media (max-width: 1200px) {
// extend выберет этот селектор
.myClass {
display: none;
}
}
}
// вызываем псевдокласс extend внутри глобальной области видимости
.global:extend(.myClass) {
}
Псевдокласс extend
, вызванный на верхнем уровне (в глобальной области видимости) позволяет выбрать все селекторы, включая селекторы находящиеся внутри вложенных правил.
Результат компиляции:
@media screen {
.myClass,
.global {
display: flex;
}
}
@media screen and (max-width: 1200px) {
.myClass,
.global {
display: none;
}
}