Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

css - Build list of selectors with LESS

General problem

I have media queries, where I change certain text elements like so:

body.single .entry-content p,
body.single .entry-content ul,
body.single .entry-content ol,
body.single .entry-content table {
    line-height: 1.8;
}

How can I use LESS to build just the list of selectors, not the corresponding CSS values?

My attempt

I'm not looking for this answer or this answer, which include the CSS in the function. Instead, I imagine it used like this:

/* .selector-list() { ... } */

@text-elements: p, ul, ol, table;

.selector-list("body.single .entry-content", @text-elements);
@selector-list {
    line-height: 1.8;
}

It would essentially build a stringified list by prepending the parent ("body.category") and appending a comma to each element from a set. Here's what I've tried just to test output:

@text-elements: p, ul, ol, table;

.selector-list(@parent, @children, @i:1, @list:"") when (@i <= length(@children)) {
    @child: extract(@children, @i);
    @selector-list: "@{list} @{parent} @{child},";
    .selector-list(@parent, @children, (@i + 1), @selector-list);
}

.selector-list("body.single .entry-content", @text-elements);

section {
    content: @selector-list; // yields only " body.single .entry-content p,"
}

Why doesn't @selector-list build past the first element? I don't entirely understand when something is printed/returned, so perhaps I'm going about this all the wrong way?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As already mentioned your attempt is almost there, it does not work because of variable visibility rules. Notice that each .selector-list iteration defines new @selector-list variable which have higher precedence in the current scope but does not override @selector-list variables for the outer scope (i.e. scopes of the previous .selector-list iterations and above). So when you use @selector-list after the initial .selector-list call you get the value set in the "highest" .selector-list iteration (i.e. the first one with @i = 1).

To "return" a value from the last iteration of a recursive loop you need to define a variable with this value only within that last iteration. Usually the simplest way to do this is to provide a "terminal" mixin (i.e. a specialization for the last call of the recursive mixin). In fact you would need such terminal anyway to handle the final comma of the list. E.g.:

#1

.selector-list(@parent, @children, @i: 1, @list: "") when (@i < length(@children)) {
    @child: extract(@children, @i);
    .selector-list(@parent, @children, (@i + 1), "@{list} @{parent} @{child},");
}

.selector-list(@parent, @children, @i, @list) when (@i = length(@children)) {
    @child: extract(@children, @i);
    @selector-list: e("@{list} @{parent} @{child}");
}

// usage:

@text-elements: p, ul, ol, table;

.selector-list("body.single .entry-content", @text-elements);
@{selector-list} {
    line-height: 1.8;
}

-

#2 Same as above just slightly "optimized":

.selector-list(@parent, @children, @i: length(@children), @list...) when (@i > 1) {
    .selector-list(@parent, @children, (@i - 1),
        e(", @{parent}") extract(@children, @i) @list);
}

.selector-list(@parent, @children, 1, @list) {
    @selector-list: e(@parent) extract(@children, 1) @list;
}

// usage:

@text-elements: p, ul, ol, table;

.selector-list("body.single .entry-content", @text-elements);
@{selector-list} {
    line-height: 1.9;
}

-

#3 Speaking of the use-case in general, a "string-based selector manipulation" is not always a good idea in context of Less. The main problem is that Less does not treat such strings as "native" selectors and most of advanced Less features won't work with them (e.g. Less won't recognize ,, & and similar elements there so such rules can't be nested, extend also can't see such selectors etc. etc.). An alternative, more "Less-friendly" approach is to define such list as a mixin rather than a variable, e.g.:

.text-elements(@-) {p, ul, ol, table {@-();}}

body.single .entry-content { 
    .text-elements({
        line-height: 1.8;
    });
}

and (when you also need to reuse body.single .entry-content /.text-elements/):

.text-elements(@-) {
    p, ul, ol, table 
        {@-();}}

.selector-list(@-) {
    body.single .entry-content {
        .text-elements(@-);
    }
}

.selector-list({
    line-height: 1.9;
});

etc.

-

P.S. Also, speaking in even more general, don't miss that in Less a media query may be put into selector ruleset, so depending on a use-case it's also often more easy to write a list of selectors once and set media depended styles inside it (i.e. doing it in opposite to the standard CSS method where you have to repeat same selector(s) for each media query).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...