2

I've been using CSS for stuff like this for a while, but it seems this one specific scenario doesn't work how it should.

What I want to have a list like you would in a spreadsheet, with each line alternating from a light grey to a slightly darker grey.

Spreadsheet lines 1-9, with alternating colors

<style>
  .spreadsheet {
    display: grid;
    grid-auto-flow: row;
  }
  .spreadsheet > div {
    padding: 0.5rem;
  }
  .spreadsheet > :first-of-type {
    border-radius: 1rem 1rem 0 0;
  }
  .spreadsheet > :only-of-type {
    border-radius: 1rem;
  }
  .spreadsheet > :last-of-type {
    border-radius: 0 0 1rem 1rem;
  }
  .spreadsheet > :nth-of-type(odd) {
    background-color: #fafafa
  }
  .spreadsheet > :nth-of-type(even) {
    background-color: #eaeaea
  }
</style>

<div class="spreadsheet">
  <div>Line 1</div>
  <div>Line 2</div>
  <div>Line 3</div>
  <div>Line 4</div>
  <div>Line 5</div>
  <div>Line 6</div>
  <div>Line 7</div>
  <div>Line 8</div>
  <div>Line 9</div>
</div>

Everything above works just fine, as anticipated. Now here's my issue... In my scenario, I'm using JavaScript to change the classes of certain lines, based on a filter. With this, I simply want to set the display of lines that are filtered out to none, whilst keeping the same pattern of alternating colors as before. This should also render the first and last lines to have rounded borders.

Spreadsheet lines 2, 5, 6-7, and 9 with incorrect alternating colors

<style>
  .spreadsheet {
    display: grid;
    grid-auto-flow: row;
  }
  .spreadsheet > div {
    padding: 0.5rem;
  }
  .spreadsheet > :not(.hidden):first-of-type {
    border-radius: 1rem 1rem 0 0;
  }
  .spreadsheet > :not(.hidden):only-of-type {
    border-radius: 1rem;
  }
  .spreadsheet > :not(.hidden):last-of-type {
    border-radius: 0 0 1rem 1rem;
  }
  .spreadsheet > :not(.hidden):nth-of-type(odd) {
    background-color: #fafafa
  }
  .spreadsheet > :not(.hidden):nth-of-type(even) {
    background-color: #eaeaea
  }
  .hidden {
    display: none;
  }
</style>

<div class="spreadsheet">
  <div class="hidden">Line 1</div>
  <div>Line 2</div>
  <div class="hidden">Line 3</div>
  <div class="hidden">Line 4</div>
  <div>Line 5</div>
  <div>Line 6</div>
  <div>Line 7</div>
  <div class="hidden">Line 8</div>
  <div>Line 9</div>
</div>

You'll notice, Line 1 is hidden, so Line 2 should have a background-color of #fafafa and a border-radius of 1rem 1rem 0 0, but it has neither. This is very obvious when the same colored lines are right next to each other.

Any ideas on how I can accomplish this?

5
  • 2
    You won't be able to do this is pure CSS as CSS doesn't know how to ignore elements while counting. The best way might be to append your child at the end of your list when it is hidden or something, or remove the element entirely. Dec 20, 2022 at 15:08
  • @somethinghere Thanks for letting me know. That makes sense. I was hoping to not have to use JavaScript to remove elements, just to save the hassle of adding them back in, but it makes sense.
    – andrilla
    Dec 20, 2022 at 15:19
  • 1
    If you wanna keep it simple, maybe just insert a span, place it where the li you want to remove is, and then add the lit in there. Then use > *:nth-of-type selectors so you only count the li. It changes nothing to your list in essense, and all you need to do to show something is reinsert the li where the span is and remove the span. Just an idea though. Dec 20, 2022 at 15:22
  • @somethinghere Not a bad idea. I this I'll try that.
    – andrilla
    Dec 20, 2022 at 15:29
  • If you're using JS to set those classes, you could also add an even or odd class to the visible elements when you're in there. No need to remove anything from the DOM.
    – ajm
    Dec 20, 2022 at 15:34

2 Answers 2

1

This snippet avoids the question of CSS trying to 'decide' whether its an odd or even element, and avoids the need for extra JS.

It just puts the background as a linear-gradient, repeating, on the parent element.

Likewise, the border radius is on the parent too so there is no need to work out whether an element is the last visible one or not:

* {
  margin: 0;
}

.spreadsheet {
  display: grid;
  grid-auto-flow: row;
  border-radius: 1rem;
  background-image: linear-gradient(#fafafa 0 2rem, #eaeaea 2rem 4rem);
  background-size: 100% 4rem;
}

.spreadsheet>div {
  padding: 0.5rem;
  line-height: 1rem;
}
<div class="spreadsheet">
  <div>Line 1</div>
  <div>Line 2</div>
  <div>Line 3</div>
  <div>Line 4</div>
  <div>Line 5</div>
  <div>Line 6</div>
  <div>Line 7</div>
  <div>Line 8</div>
  <div>Line 9</div>
</div>
With some children display: none;
<div class="spreadsheet">
  <div style="display: none;">Line 1</div>
  <div>Line 2</div>
  <div>Line 3</div>
  <div style="display: none;">Line 4</div>
  <div>Line 5</div>
  <div style="display: none;">Line 6</div>
  <div>Line 7</div>
  <div>Line 8</div>
  <div>Line 9</div>
</div>

Note however (as pointed out by @somethinghere) this only works for single line entries - i.e. such as you might get from a simple spreadsheet on a wide enough viewport.

3
  • This really breaks if your line gets broken, no? Dec 21, 2022 at 7:59
  • @somethinghere Yes, correct. It depends on what you want in terms of looking 'like in in a spreadshheet' - OK for simple one-line layouts, not otherwise.
    – A Haworth
    Dec 21, 2022 at 8:38
  • Thank you! This is a perfect solution for what I needed. I did have to adjust the linear-gradient and background-size a tiny bit to get it to fit correctly, but this is great. background-image: linear-gradient(#fafafa 50%, #eaeaea 50%); background-size: 100% 4.25rem;
    – andrilla
    Dec 21, 2022 at 16:08
0

I'm not quite sure if this will work, but wouldn't it be easier to do something like this in JS:

document.getElementById(id).style.visibility = "hidden";

To hide or display lines and then later you can apply the changes with:

let flag = True;

for (const line in lines) { // Lines is an array with your lines
  if (line.style.display != none){ // If the line is being displayed
    if(flag){ // Flag acts as a line color control
      line.style...// Make it so it displays a white line
    } else {
      line.style...// Make it so it displays a gray line
    }
    flag = !flag; // We switch the color of the line
  }
}

And assigning a different id for each line, instead of changing the classes? I think you might be overcomplicating the entire thing.

5
  • Setting a style attribute does that same thing as setting a class with that styling.
    – andrilla
    Dec 20, 2022 at 15:28
  • Yeah, but with changing the class you get nothing, by changing the display you get the functionality that the OP desires. You don't need to change the class to hidden and use an overcomplicated css when you can solve the problem with one JS line and by placing ids to the HTML. Dec 20, 2022 at 15:34
  • 1
    Ypu do not. A hidden item is still counted by css’s nth-of selectors. Dec 21, 2022 at 7:58
  • What about the new solution I proposed (edited old one)? I think they key might come with using wisely Javascript instead of overcomplicating CSS. Css should be clear and easy, scripting is where you should put most of your effort. Dec 21, 2022 at 9:13
  • 2
    Like I mentioned in the comments, best to just remove this items from the list and only place them back if you need them. It will ignore any objects, save m emory and allow your CSS to be rather clean. It does require some management. Dec 21, 2022 at 9:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.