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

Ссылка на родителя селектора

Благодаря вложенности, мы можем писать бесподобный структурированный код, который будет понятен всем разработчикам и даже тому, кто его написал. Но сейчас у нас на повестке дня стоит вопрос о том, как в этой модели вложенности работать с псевдоклассами, псевдоэлементами и комбинированными селекторами. Для этого в Less есть специальный символ — & (родительский селектор).

Символ & играет особую и важную роль в Less. С помощью этого символа можно обращаться к родителю текущего селектора, а также сшивать, объединять их и создавать внутри них области видимости (подробнее в главе 3).

Прямое объединение селекторов

Родительский селектор может использоваться для объединения любых селекторов, псевдоэлементов и псевдоклассов.

Пример 2.3.1

Наиболее часто, символ & применяется для добавления псевдоклассов к селекторам. Например, с помощью него можно добавить эффект при наведении, используя псевдокласс :hover. Посмотрите на следующий пример:

a {
  color: #777;
  text-decoration: none;

  &:hover {
    color: #a31515;
  }
}

После компиляции селектор a и псевдокласс :hover будут объединены, а их свойства поделены между ними в соответствии с их объявлениями по вложенности:

a {
  color: #777;
  text-decoration: none;
}

a:hover {
  color: #a31515;
}

Пример 2.3.2

Как я уже говорил раньше, объединять можно и селекторы с другими селекторами. Для примера ниже приводится код объединения .class-1 с .class-2.

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

  &.class-2 {
    color: #000;
  }
}

На выходе компилятора получается сдвоенный селектор:

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

Обратите внимание, что препроцессор не запрещает использовать стандартный синтаксис .class-1.class-2 {}. Но при таком стиле записи теряется весь смысл и шарм, который нам дарит препроцессор.

Обратное объединение селекторов

У автомобилей есть передний и задний ход, такой же функционал реализован и у символа &. Такие возможности позволяют писать более гибкий код, когда нужно добавить какой-то селектор перед другим селектором. Вместо того, чтобы писать новую структуру, можно просто использовать этот функционал.

Пример 2.3.3

Примером может послужить поддержка IE, когда с помощью JavaScript в тег body или html добавляются классы .ie6, .ie7 и им подобные. Возможность использования обратного объединения позволяет писать более гибкий код, когда нужно изменить контекст применения селектора.

Представим, что нашему проекту требуется поддержка IE7, который не умеет работать со свойством border-image. А по дизайну в любом случае должна быть рамка между двумя элементами, иначе они превратятся в тыкву.

На деле имеем такой код:

.item-card {
  background-color: #f5f5f5;
  border-image: url('images/bg-image.png') 30 round round;
}

Вместо того, чтобы писать новый класс .ie7 .item-card {} и плодить сложно поддерживаемую структуру, можно использовать принцип обратного объединения.

.main {
  .item-card {
    background-color: #f5f5f5;
    border-image: url('images/bg-image.png') 30 round round;

    .ie7 & {
      border: 1px solid #a31515;
    }
  }
}

Во время компиляции, селектор, у которого имеется ссылка на родителя справа от имени, будет опущен ниже по древу вложенности вне зависимости от глубины вложенности. То есть при обратном объединении родительский селектор всегда ссылается на корневой селектор всего объявления.

.main .item-card {
  background-color: #f5f5f5;
  border-image: url('images/bg-image.png') 30 round round;
}
.ie7 .main .item-card {
  border: 1px solid #a31515;
}

Внимание!

Если написать .ie7 & без пробела, то после компиляции селектор будет комбинированным, а не вложенным:

.item-card {
  background-color: #f5f5f5;
  border-image: url('images/bg-image.png') 30 round round;
}
.ie7.item-card {
  border: 1px solid #a31515;
}

Сшивание (склеивание) селекторов

Не редко требуется производить операцию склеивания имён текущего и родительского селектора. Такая практика применяется при создании новых классов на основе старого. Кроме того, такая операция просто необходима тем людям, кто использует методологию БЭМ (и ей подобные) при написании стилей.

Пример 2.3.4

Стоит задача стилизации кнопок для проекта. Кнопки имеют различные цвета, в зависимости от действия и контекста использования. Изначально кнопки имеют серый цвет. Кнопка, предназначенная для добавления новой записи имеет зелёный цвет, а для удаления — красный.

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

.button {
  background-color: #ddd;
  color: #000;
}

.button-add {
  background-color: green;
  color: #fff;
}

.button-remove {
  background-color: red;
  color: #fff;
}

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

.button {
  background-color: #ddd;
  color: #000;

  &-add {
    background-color: green;
    color: #fff;
  }

  &-remove {
    background-color: red;
    color: #fff;
  }
}

А ещё этот код можно оптимизировать, указав сначала общий для двух селекторов цвет текста, а уже потом индивидуальный цвет фона. Но не стоит забывать, что такая оптимизация не имеет никакого эффекта в случае, если после компиляции код будет минифицирован, так как практически любой современный минификатор умеет группировать селекторы с общими свойствами.

.button {
  background-color: #ddd;
  color: #000;

  &-add,
  &-remove {
    color: #fff;
  }

  &-add {
    background-color: green;
  }
  &-remove {
    background-color: red;
  }
}

Многократное и комбинированное использование

Символ & может использоваться в селекторе больше одного раза. Это даёт возможность несколько раз ссылаться на родительский селектор, но не писать его имя.

Пример 2.3.5

Немного академический пример, который не несёт в себе смысловой нагрузки, но всё таки отображает суть происходящего:

.header {
  .item {
    & + & {
      color: red;
    }

    & & {
      color: green;
    }

    && {
      color: blue;
    }

    &,
    &-box {
      color: yellow;
    }
  }
}

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

.header .item + .header .item {
  color: red;
}
.header .item .header .item {
  color: green;
}
.header .item.header .item {
  color: blue;
}
.header .item,
.header .item-box {
  color: yellow;
}

Такое использование символа & практически нигде не используется (кроме случая &, &-box). Если вы его встретите где-нибудь, то будьте уверены, что сегодня ваш день.

Пример 2.3.6

Из примера 2.3.5 следует, что с помощью такого вот взаимодействия текущего селектора с родительским, можно творить неведомые ранее чудеса. Это может пригодиться тогда, когда нужно комбинировать селекторы между собой. Наиболее ярко суть происходящего описывает пример из документации.

ul,
li {
  border-top: 2px dotted #366;

  & + & {
    border-top: 0;
  }
}

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

ul,
li {
  border-top: 2px dotted #366;
}

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

ul + ul,
ul + li,
li + ul,
li + li {
  border-top: 0;
}

Но что будет, если указать три вызова родительского селектора (& + & + &)? Правильно, будет комбинация доступного перечня по трём позициям:

ul + ul + ul,
ul + ul + li,
ul + li + ul,
ul + li + li,
li + ul + ul,
li + ul + li,
li + li + ul,
li + li + li {
  border-top: 0;
}

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

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

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