Группировка селекторов. Назначение псевдо-класса :extend() в Less
Расширение селекторов (Extend) – это псевдо-класс в Less, который объединяет тот селектор, на который оно наложено, с тем, который совпадает со ссылками.
Проще говоря, псевдокласс :extend()
автоматизирует следующий процесс:
- Отыскать селекторы с одинаковыми свойствами.
- Выбрать базовый селектор.
- Перечислить все найденные селекторы в объявлении базового селектора.
- Все новые селекторы добавить в список селекторов базового объявления.
Появилось в v1.4.0
nav ul {
&:extend(.inline);
background: blue;
}
В указанном выше правиле селектор :extend
будет применять «расширяющий селектор» (nav ul
) на класс .inline
где бы ни появился класс .inline. Блок декларации останется как есть, без каких-либо ссылок к расширению (т.к. расширение – это не CSS).
Например:
nav ul {
&:extend(.inline);
background: blue;
}
.inline {
color: red;
}
В результате получим:
nav ul {
background: blue;
}
.inline,
nav ul {
color: red;
}
Обратите внимание, как селектор nav ul:extend(.inline)
преобразовывается на выходе как nav ul
- расширение удаляется перед выводом, а блок селектора остается неизменным. Если в тот блок не вложено никаких свойств, он удаляется из вывода (но расширение по-прежнему может влиять на другие селекторы).
Синтаксис :extend()
Псевдокласс :extend()
либо присоедине к селектору, либо размещен в наборе правил. Он выглядит как псевдо-класс с параметром селектора, опционально следующим за ключевым словом all
:
Например:
.a:extend(.b) {}
// блок сверху действует точно так же, как и блок снизу
.a {
&:extend(.b);
}
.c:extend(.d all) {
// расширяет все варианты ".d", такие как ".x.d" или ".d.x"
}
.c:extend(.d) {
// расширяет только те варианты, в которых
// селектор будет только выводом, как просто ".d"
}
Расширение селекторов может содержать один или несколько классов, разделенных запятыми. Например:
.e:extend(.f) {}
.e:extend(.g) {}
// код ниже равнозначен верхнему
.e:extend(.f, .g) {}
Расширение селекторов, присоединенный к селектору
Расширение селекторов, присоединенное к селектору, выглядит как обычный псевдо-класс с параметром в виде селектора. Селектор может содержать несколько extend, но все они должны стоять в конце селектора.
- Extend после селектора:
pre:hover:extend(div pre)
.
- Между селектором и extend можно ставить пробел:
pre:hover :extend(div pre)
.
- Можно ставить несколько extend:
pre:hover:extend(div pre):extend(.bucket tr)
- То же самое, что и pre:hover:extend(div pre, .bucket tr)
- А вот так нельзя:
pre:hover:extend(div pre).nth-child(odd)
. Extend должен стоять в конце.
Если набор правил содержит несколько селекторов, у любого из них может быть Extend. Несколько селекторов с extend в одном наборе правил:
.big-division,
.big-bag:extend(.bag),
.big-bucket:extend(.bucket) {
// body
}
Контекстная группировка
Расширение селекторов (Extend) можно размещать внутри набора правил с помощью синтаксиса &:extend(selector)
. Размещение псевдокласса :extend()
в теле является своеобразным ярлыком для размещения его в каждом селекторе этого набора правил.
Extend внутри набора правил:
pre:hover,
.some-class {
&:extend(div pre);
}
То же самое, что и добавление extend после каждого селектора:
pre:hover:extend(div pre),
.some-class:extend(div pre) {}
Группировка внутренних селекторов
Extend может группировать внутренние селекторы. Например:
.bucket {
tr { // внутренний набор правил с целевым селектором
color: blue;
}
}
.some-class:extend(.bucket tr) {} //внутренний набор правил распознан
В результате получим:
.bucket tr,
.some-class {
color: blue;
}
По сути, расширение просматривает скомпилированный CSS, а не оригинальный Less-код. Например:
.bucket {
tr & { // внутренний набор правил с целевым селектором
color: blue;
}
}
.some-class:extend(tr .bucket) {} // внутренний набор правил распознан
Получаем:
tr .bucket,
.some-class {
color: blue;
}
Точное совпадение с Extend
Расширение селекторов (Extend) по умолчанию ищет точные совпадения между селекторами. Неважно, использует ли селектор «ведущую звезду». Неважно, что два N-ных выражения имеют одинаковое значение, они должны быть одинаково сформированы, чтобы совпадать. Единственное исключение – кавычки в селекторе атрибута, т.к. Less считает, что у них одинаковое значение и они совпадают.
Например:
.a.class,
.class.a,
.class > .a {
color: blue;
}
.test:extend(.class) {} // this will NOT match the any selectors above
Ведущая звезда не играет роли. Селекторы *.class
и .class
эквивалентны, но extend не будет сравнивать их:
*.class {
color: blue;
}
.noStar:extend(.class) {} // это НЕ БУДЕТ будет соответствовать селектору *.class
В результате получим:
*.class {
color: blue;
}
Порядок псевдо-классов имеет значение. Cелекторы link:hover:visited
и link:visited:hover
соответствуют одинаковому набору элементов, но extend рассматривает их как разные:
link:hover:visited {
color: blue;
}
.selector:extend(link:visited:hover) {}
В результате получим:
link:hover:visited {
color: blue;
}
N-ное выражение
Форма N-ного выражения не имеет значения. N-ные выражения 1n+3
и n+3
эквивалентны, 3эквивалентны, но extend не будет их сравнивать:
:nth-child(1n+3) {
color: blue;
}
.child:extend(:nth-child(n+3)) {}
В результате получим:
:nth-child(1n+3) {
color: blue;
}
Тип кавычек в селекторе атрибута не важен. Все следующие записи эквивалентны.
[title=identifier] {
color: blue;
}
[title='identifier'] {
color: blue;
}
[title="identifier"] {
color: blue;
}
.noQuote:extend([title=identifier]) {}
.singleQuote:extend([title='identifier']) {}
.doubleQuote:extend([title="identifier"]) {}
В результате получим:
[title=identifier],
.noQuote,
.singleQuote,
.doubleQuote {
color: blue;
}
[title='identifier'],
.noQuote,
.singleQuote,
.doubleQuote {
color: blue;
}
[title="identifier"],
.noQuote,
.singleQuote,
.doubleQuote {
color: blue;
}
"all" в Расширении селекторов
Когда вы указываете ключевое слово «all» последним в аргументе псевдокласса :extend()
, - это говорит Less сравнить этот селектор с частью другого селектора. Селектор скопируется и совпавшая часть селектора только тогда заменится расширением, создав новый селектор. Например:
.a.b.test,
.test.c {
color: orange;
}
.test {
&:hover {
color: green;
}
}
.replacement:extend(.test all) {}
В результате будем иметь такие CSS стили:
.a.b.test,
.test.c,
.a.b.replacement,
.replacement.c {
color: orange;
}
.test:hover,
.replacement:hover {
color: green;
}
Вы можете считать, что этот вариант операции по сути совершает обычный поиск и замену.
Интерполяция селекторов с Расширением селекторов
Расширение селекторов НЕ может сравнивать селекторы с переменными. Если селектор содержит переменную, расширение проигнорирует ее. Для этого существует запрос функции ожидания, но это не простая замена. Однако, расширение можно присоединить к интерполированному селектору.
Селектор с переменной не будет сравниваться:
@variable: .bucket;
@{variable} { // интерполированный селектор
color: blue;
}
.some-class:extend(.bucket) {} // ничего не происходит: не найдено совпадения
А расширение с переменной в целевом селекторе ни с чем не совпадает:
.bucket {
color: blue;
}
// интерполированный селектор
// ни с чем не совпадает:
.some-class:extend(@{variable}) {}
@variable: .bucket;
Оба предыдущих примера компилируются в:
.bucket {
color: blue;
}
Однако, :extend
, присоединенное к интерполированному селектору будет работать:
.bucket {
color: blue;
}
@{variable}:extend(.bucket) {}
@variable: .selector;
Предыдущий LESS компилируется в следующий CSS:
.bucket, .selector {
color: blue;
}
Расширение селекторов, написанное внутри медиа-декларирования должно совпадать только с селекторами внутри того же самого медиа-декларирования:
@media print {
.screenClass:extend(.selector) {} // расширение внутри media
.selector { // совпадение есть, т.к. внутри того же media
color: black;
}
}
.selector { // набор правил в верхней части таблицы
// стилей – расширение селекторов его игнорирует
color: red;
}
@media screen {
.selector { // набор правил внутри другого media –
// расширение селекторов его игнорирует
color: blue;
}
}
Компилируется в:
@media print {
.selector,
.screenClass {
/* набор правил внутри того же media
был расширен */
color: black;
}
}
.selector {
/* набор правил в верхней части
таблицы стилей проигнорирован */
color: red;
}
@media screen {
.selector {
/* набор правил внутри другого media
проигнорирован */
color: blue;
}
}
Компилируется в:
@media screen and (min-width: 1023px) {
.selector {
/* набор правил внутри другого встроенного
media проигнорирован */
color: blue;
}
}
Расширения селекторов верхнего совпадают со всем, включая селекторы внутри встроенного медиа:
@media screen {
.selector {
/* набор правил внутри встроенного media – расширение
селекторов верхнего уровня работает */
color: blue;
}
@media (min-width: 1023px) {
.selector {
/* набор правил внутри встроенного media – расширение
селекторов верхнего уровня работает */
color: blue;
}
}
}
/* расширение селекторов верхнего уровня совпадает со всеми: */
.topLevel:extend(.selector) {}
Компилируется в:
@media screen {
.selector,
.topLevel {
/* набор правил внутри медиа был расширен */
color: blue;
}
}
@media screen and (min-width: 1023px) {
.selector,
.topLevel {
/* набор правил внутри встроенного media был расширен */
color: blue;
}
}
Случаи использования расширений