Generating selector lists in LESS

2019-05-25 08:01发布

问题:

Live demo here: http://codepen.io/KenPowers/pen/Ddfqh

Consider the following LESS code:

// Hide all list items by default and make internal labels have pointer cursors
li {
  display: none;
  label {
    cursor: pointer;
  }
}

// This function generates selectors and corresponding css
.gen (@tag) {
  @sel: ~"#@{tag}:checked";
  @{sel} {
    & ~ ul > li[data-index~=@{tag}] {
      display: list-item;
    }
    & ~ ul > li > label[for=@{tag}] {
      color: red;
    }
  }
}

// Generate required selectors and css
.gen("foo");
.gen("bar");
.gen("baz");

It generates the following CSS:

li {
  display: none;
}
li label {
  cursor: pointer;
}
#foo:checked ~ ul > li[data-index~="foo"] {
  display: list-item;
}
#foo:checked ~ ul > li > label[for="foo"] {
  color: red;
}
#bar:checked ~ ul > li[data-index~="bar"] {
  display: list-item;
}
#bar:checked ~ ul > li > label[for="bar"] {
  color: red;
}
#baz:checked ~ ul > li[data-index~="baz"] {
  display: list-item;
}
#baz:checked ~ ul > li > label[for="baz"] {
  color: red;
}

However, a more compressed output may look like the following:

li {
  display: none;
}
li label {
  cursor: pointer;
}
#foo:checked ~ ul > li[data-index~="foo"],
#bar:checked ~ ul > li[data-index~="bar"],
#baz:checked ~ ul > li[data-index~="baz"] {
  display: list-item;
}
#foo:checked ~ ul > li > label[for="foo"],
#bar:checked ~ ul > li > label[for="bar"],
#baz:checked ~ ul > li > label[for="baz"] {
  color: red;
}

Is there a way I can modify the original LESS to generate the second CSS listing?

回答1:

In Less 1.4.0+ you can :extend a placeholder class:

.dp-list-item {
  display: list-item;
}
.label-color {
  color: red;
}

// This function generates selectors and corresponding css
.gen (@tag) {
  @sel: ~"#@{tag}:checked ~ ul > li";
  @{sel} {
    &[data-index~=@{tag}]:extend(.dp-list-item){}
    & > label[for=@{tag}]:extend(.label-color){}
  }
}

// Generate required selectors and css
.gen("foo");
.gen("bar");
.gen("baz");

Forked pen

Generated CSS:

.dp-list-item,
#foo:checked ~ ul > li[data-index~="foo"],
#bar:checked ~ ul > li[data-index~="bar"],
#baz:checked ~ ul > li[data-index~="baz"] {
  display: list-item;
}
.label-color,
#foo:checked ~ ul > li > label[for="foo"],
#bar:checked ~ ul > li > label[for="bar"],
#baz:checked ~ ul > li > label[for="baz"] {
  color: red;
}

As you can see, the only drawback is that both classes which I've used as placeholders will be in the generated CSS. I believe this can't be worked around until Less implements something akin to Sass' placeholder selectors.

ps. I've omitted the global li rules which are not part of the mixin for brevity.


As per OP request, here is a Sass (with .scss syntax) equivalent:

//placeholder selectors
%dp-list-item {
  display: list-item;
}
%label-color {
  color: red;
}

// This function generates selectors and corresponding css
@mixin gen($tag) {
  ##{$tag}:checked ~ ul > li {
    &[data-index~=#{$tag}] {
      @extend %dp-list-item;
    }
    & > label[for=#{$tag}] {
      @extend %label-color;
    }
  }
}

// Generate required selectors and css
@each $item in (foo bar baz) {
  @include gen($item);
}

Demo

Generated CSS:

#foo:checked ~ ul > li[data-index~=foo], #bar:checked ~ ul > li[data-index~=bar], #baz:checked ~ ul > li[data-index~=baz] {
  display: list-item;
}

#foo:checked ~ ul > li > label[for=foo], #bar:checked ~ ul > li > label[for=bar], #baz:checked ~ ul > li > label[for=baz] {
  color: red;
}

You can see that Sass' syntax is rather verbose when compared to Less. Sass also has some nice features such as control directives and excellent interpolation which I've applied in the example above.