Вложенные примеси в LESS-поведении

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

Я знаю, сбивает с толку, вот простой пример (не рабочий код, просто концепция):

МЕНЬШЕ

.foo(@x) {
    width: @x;

    .foo(@y) {
        width: @y/@x;
    }
}

.a {
    .foo(20px);

    .b { 
        .foo(2);
    }
}

Вывод CSS

.a {
    width: 20px;
}

.a .b {
    width: 10px;
}

Когда я делаю это, вызов .foo(2) на .b дает компиляцию в width: 2.

Это должно быть так по замыслу, или я что-то неправильно понимаю в синтаксисе? Кроме того, я подхожу к проблеме с совершенно неправильной точки зрения, и, возможно, есть гораздо более простое решение, которое я не рассматриваю?

РЕДАКТИРОВАТЬ

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

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

МЕНЬШЕ

.foo(@x) {
    width: @x;

    .foo(@y) {
        width: (@x/@y);
    }
}

.a {
    .foo(100px);

    .b { 
        .foo(2px);

        .c {
            .foo(5px);
            /* ...and so on */
        }
    }
}

Вывод CSS

.a {
    width: 100px;
}

.a .b {
    width: 50px;
}

.a .b .c {
    width: 10px;
}

Вместо этого я получаю:

.a .b .c {
    width: 50px;
}

Я попытался изменить LESS следующим образом:

.foo(@x) {
    width: @x;

    .foo(@y) {
        @x:    (@x/@y)
        width: (@x);
    }
}

Но я получаю синтаксическую ошибку для определения рекурсивной переменной. По-видимому, LESS не допускает таких определений, как:

@a: 1;
@a: (@a+1);

person Sunyatasattva    schedule 14.04.2013    source источник


Ответы (1)


Я думаю, что основной проблемой здесь будет @y/@x. Попробуйте @x/@y, и это должно дать что-то более ожидаемое ;-)

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

1. Решение, которое работало на less2css.org с LESS > 1.3.1 (с использованием ваших вложенных миксинов)

В противном случае я думаю, что приведенный выше код действительно делает то, что вы хотите, на less2css.org. Как и в LESS 1.4, вам нужно быть осторожным с математикой, так как по умолчанию она должна быть заключена в скобки.

Если вы сейчас просто вызовете такой миксин для невложенных правил, например

.b { .foo(@y); }

вам нужно использовать единицы измерения во входной переменной или добавить unit() в свой миксин, иначе вы получите только то число, которое вы ввели, например 2:

.foo(@x) {
    width: unit(@x,px);
    .foo(@y) {
        width: (@x/@y);
    }
}

.a {
    .foo(20px);
    .b{
        .foo(2);
     }
}
.c {
    .foo(2);
}

выведет CSS:

.a {
  width: 20px;
}
.a .b {
  width: 10px;
}
.c {
  width: 2px;
}

Вы можете стать еще более изобретательным и проверить у охранников, имеет ли атрибут в подклассе пиксели как единицу, чтобы вы также могли вкладывать классы, в которых вы не передаете фактор:

.foo(@x) {
    width: @x;
    .foo(@y) when (ispixel(@y)) {
        width: @y;
    }
    .foo(@y) when not (ispixel(@y)) {
        width: (@x/@y);
    }
}

Однако тестирование этого вложенного миксина работает только на less2css.org, но не на jsbin.com, lesstester.com и некоторых других сервисах [где вам нужно вызвать вложенный (второй) уровень миксина с помощью .foo .too, чтобы применить второй уровень стилизации от миксина].

Поэтому я предлагаю альтернативный подход, который я протестировал, и он, кажется, работает на всех упомянутых страницах с использованием компиляторов less-css с less> 1.2.

2. Решение с использованием охранников с ispixel, которое должно работать на большинстве установок LESS > 1.2.

Вместо вашего вложенного миксина вы можете создать два миксина, которые основаны на проверке охранников на пиксели как единицу измерения.

  • если атрибут @x указан в пикселях => вернуть width:@x; и присвоить @x переменной @width
  • если атрибут @x не в пикселях => вернуть width:@width/@x; (примечание: @width необходимо предварительно назначить, сначала вызвав миксин с @x в px)

пример МЕНЬШЕ:

.foo(@x) when (ispixel(@x)) {
        width:@x;
        @width:@x;
}
.foo(@x) when not (ispixel(@x)) {
        width: (@width/@x);
}


.a, .b {
  background-color: #ddd;
  height: 100px;
}
.a {
  .foo(100px);
  .b {
    background-color: red;
    .foo(2);
  }
}

выходной CSS:

.a, .b {
  background-color: #ddd;
  height: 100px;
}
.a {
  width: 100px;
}
.a .b {
  background-color: red;
  width: 50px;
}

Отличается от вашего подхода, но, возможно, немного более прямолинеен и, кажется, работает хорошо.

Редактировать:

Так как вы не хотите различать ввод с модулем и ввод без модуля, я могу думать только о вызове двухпараметрического миксина, где один параметр используется для базы (width в вашем случае) а второй как фактор, который по умолчанию равен 1. И теперь вы можете вызывать его рекурсивно столько раз, сколько захотите.

МЕНЬШЕ:

.foo(@x,@y:1){
    width:unit(@parent,px);
    @parent:(@x/@y);
}


.a {
    .foo(100px);
    .b{
        .foo(@parent,2px);
        .c{
            .foo(@parent,5px);
            .d{
                .foo(@parent,0.05);
            }
        }
    }
}

вывод CSS:

.a {
  width: 100px;
}
.a .b {
  width: 50px;
}
.a .b .c {
  width: 10px;
}
.a .b .c .d {
  width: 200px;
}

Так что теперь не имеет значения, какая единица измерения у входа, потому что вы можете назначить желаемую единицу для вывода в миксине. Когда вы вызываете миксин, он перезаписывает переменную @parent для текущей области видимости, которая затем наследуется во вложенных правилах, где вы можете использовать ее в качестве параметра ширины @x при повторном вызове миксина. Это должно дать вам желаемый результат.

person Martin Turjak    schedule 14.04.2013
comment
Ахах, извините за глупую математическую ошибку, во всяком случае, моя проблема в том, что это не работает. Как вы можете видеть в этом маленьком JSBin, он не выводит ожидаемый CSS для вложенного элемента .b. Но, скорее, он выводит первый миксин .foo(@x). Вызов .foo .foo(2px) действительно работает (хотя, должен сказать, не всегда). Но я не думаю, что в моем случае имеет смысл использовать этот синтаксис (мой случай немного сложнее, чем описанный выше) - person Sunyatasattva; 15.04.2013
comment
Благодарю вас! На самом деле проблема была в том, что я использовал LESS 1.3.0. К сожалению, я не могу подойти к проблеме с вашим вторым решением, потому что в моей реальной проблеме не используется определение пикселя в родительском и абсолютное определение в дочернем, но оба абсолютных определения. Теперь проблема в том, возможно ли, чтобы рекурсия происходила бесконечно? (См. мое редактирование для получения более подробной информации о том, что я имею в виду, я проголосовал за ответ, но не принял его, потому что, к сожалению, мои проблемы все еще остаются в силе) - person Sunyatasattva; 15.04.2013
comment
Вложенное решение, которое я дал выше, может быть вложено в столько уровней, сколько нужно, и оно отлично работает в новых версиях LESS, и я не уверен, почему вы не хотите различать ввод с единицей и ввод без единицы. Однако способ решить эту проблему в версии 1.3.0 — использовать миксин, который принимает параметр parent в качестве дополнительного аргумента. - person Martin Turjak; 17.04.2013
comment
Сейчас я использую последнюю версию LESS, не нужно поддерживать 1.3.0. Возможно, я не слишком ясно объяснил, но вы можете увидеть результат, к которому я стремлюсь, в приведенном выше примере кода. По сути, я хочу, чтобы эта рекурсия происходила бесконечно, т. е. мне не нужно иметь код LESS для каждого уровня глубины, если вы не будете писать каждый уровень своих операторов for или while. - person Sunyatasattva; 17.04.2013
comment
Хорошо, но все же я бы добавил дополнительный аргумент в миксин и присвоил новое значение переменной @parent в каждой области видимости и рекурсивно использовал его во вложенных правилах. Я добавил это решение к моему ответу выше. - person Martin Turjak; 17.04.2013