Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different styles depending if the number of checked boxes is odd or even

I'm running tests with CSS and SASS and have a series of selectors that are the same one repeated and nested several times, I want to simplify my code but don't know how to do it (it should be possible, but I can't figure it out).

The main idea is that I have a good number of radio buttons (84, in 42 sets of 2 with the same name) and want to add styles to an element depending if the number of radio buttons checked is odd or even.

To do that, right now I'm using & in SASS to generate the current selector, and nest it with the sibling selector ~ (order is not important, just the number of checked radios). This is a reduced version of the SASS code (with only 10 groups):

.rd:checked {
  & ~ &,
  & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & {
    ~ #oddeven {
      background: red; // red if even
    }
  }
  &,
  & ~ & ~ &,
  & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
  & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & {
    ~ #oddeven {
      background: green; // green if odd 
    }
  }
}

That generates the following CSS code (I add the HTML so the effect I want is visible):

.rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: red;
}

.rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: green;
}

#oddeven {
  color: white;
  background: red;
  padding: 1em;
  position: absolute;
  top: 50px;
  right: 0;
  left: 100px;
}
<input type="radio" class="rd" name="rd0" />
<input type="radio" class="rd" name="rd0" /><br/>
<input type="radio" class="rd" name="rd1" />
<input type="radio" class="rd" name="rd1" /><br/>
<input type="radio" class="rd" name="rd2" />
<input type="radio" class="rd" name="rd2" /><br/>
<input type="radio" class="rd" name="rd3" />
<input type="radio" class="rd" name="rd3" /><br/>
<input type="radio" class="rd" name="rd4" />
<input type="radio" class="rd" name="rd4" /><br/>
<input type="radio" class="rd" name="rd5" />
<input type="radio" class="rd" name="rd5" /><br/>
<input type="radio" class="rd" name="rd6" />
<input type="radio" class="rd" name="rd6" /><br/>
<input type="radio" class="rd" name="rd7" />
<input type="radio" class="rd" name="rd7" /><br/>
<input type="radio" class="rd" name="rd8" />
<input type="radio" class="rd" name="rd8" /><br/>
<input type="radio" class="rd" name="rd9" />
<input type="radio" class="rd" name="rd9" />

<div id="oddeven">
  RED background if even number of checked radios (or nothing).<br/>
  GREEN background if odd number of checked radios.
</div>

Is there a way to reduce/simplify the SASS code so it is cleaner? The way it is right now, it's easy to make a mistake and it is complicated to add new rules (for new sets of radio buttons).

And alternatively, is it possible to do it in a better way? (maybe using a mixin or actually nesting in some way). Instead of using & and ~, I tried using CSS counters, but I didn't find a way to add styles depending on the value of the counter.

NOTE: I don't want to use JavaScript, just HTML and CSS/SASS to generate the rules.

like image 630
Alvaro Montoro Avatar asked Aug 12 '18 14:08

Alvaro Montoro


2 Answers

You can generate those & ~ & ... selectors using for loop, string interpolation and concatenation:

// function to repeat string:
@function r($string, $times) { 
  $result: "";
  @if $times >= 1 {
    @for $i from 1 through $times {
      $result: $result + $string;
    }
  }
  @return $result;
}

// generate rules:
@for $n from 1 through 10 {
  .rd:checked {
    $s: r(' ~ &', $n - 1);
    $s: '&' + $s;
    @if ($n % 2 == 0) {
      #{$s} ~ #oddeven {
        background: red;
      }
    } @elseif ($n % 2 == 1) {
      #{$s} ~ #oddeven {
        background: green;
      }
    }
  }
}

#oddeven {
  color: white;
  background: red;
  padding: 1em;
  position: absolute;
  top: 50px;
  right: 0;
  left: 100px;
}

I know it doesn't produce the exact same CSS as yours, but with some more playing that can be achieved too.

Working demo:

.rd:checked ~ #oddeven {
  background: green;
}

.rd:checked ~ .rd:checked ~ #oddeven {
  background: red;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: green;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: red;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: green;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: red;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: green;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: red;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: green;
}

.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
  background: red;
}

#oddeven {
  color: white;
  background: red;
  padding: 1em;
  position: absolute;
  top: 50px;
  right: 0;
  left: 100px;
}
<input type="radio" class="rd" name="rd0" />
<input type="radio" class="rd" name="rd0" /><br/>
<input type="radio" class="rd" name="rd1" />
<input type="radio" class="rd" name="rd1" /><br/>
<input type="radio" class="rd" name="rd2" />
<input type="radio" class="rd" name="rd2" /><br/>
<input type="radio" class="rd" name="rd3" />
<input type="radio" class="rd" name="rd3" /><br/>
<input type="radio" class="rd" name="rd4" />
<input type="radio" class="rd" name="rd4" /><br/>
<input type="radio" class="rd" name="rd5" />
<input type="radio" class="rd" name="rd5" /><br/>
<input type="radio" class="rd" name="rd6" />
<input type="radio" class="rd" name="rd6" /><br/>
<input type="radio" class="rd" name="rd7" />
<input type="radio" class="rd" name="rd7" /><br/>
<input type="radio" class="rd" name="rd8" />
<input type="radio" class="rd" name="rd8" /><br/>
<input type="radio" class="rd" name="rd9" />
<input type="radio" class="rd" name="rd9" />

<div id="oddeven">
  RED background if even number of checked radios (or nothing).<br/>
  GREEN background if odd number of checked radios.
</div>
like image 132
juzraai Avatar answered Nov 02 '22 12:11

juzraai


This is currently not possible in CSS without manually doing it as you have done here.

However, when CSS4 selectors become available, you could use :nth-child() and :last-child together.

.rd:last-of-type:nth-child(even of :checked) ~ #oddeven {
  background: red;
}
.rd:last-of-type:nth-child(odd of :checked) ~ #oddeven {
  background: green;
}

#oddeven {
  color: white;
  background: red;
  padding: 1em;
  position: absolute;
  top: 50px;
  right: 0;
  left: 100px;
}
<input type="radio" class="rd" name="rd0" />
<input type="radio" class="rd" name="rd0" /><br/>
<input type="radio" class="rd" name="rd1" />
<input type="radio" class="rd" name="rd1" /><br/>
<input type="radio" class="rd" name="rd2" />
<input type="radio" class="rd" name="rd2" /><br/>
<input type="radio" class="rd" name="rd3" />
<input type="radio" class="rd" name="rd3" /><br/>
<input type="radio" class="rd" name="rd4" />
<input type="radio" class="rd" name="rd4" /><br/>
<input type="radio" class="rd" name="rd5" />
<input type="radio" class="rd" name="rd5" /><br/>
<input type="radio" class="rd" name="rd6" />
<input type="radio" class="rd" name="rd6" /><br/>
<input type="radio" class="rd" name="rd7" />
<input type="radio" class="rd" name="rd7" /><br/>
<input type="radio" class="rd" name="rd8" />
<input type="radio" class="rd" name="rd8" /><br/>
<input type="radio" class="rd" name="rd9" />
<input type="radio" class="rd" name="rd9" />

<div id="oddeven">
  RED background if even number of checked radios (or nothing).<br/>
  GREEN background if odd number of checked radios.
</div>
like image 23
Niet the Dark Absol Avatar answered Nov 02 '22 13:11

Niet the Dark Absol