0

This code is going to be used to overload the style of a page I dont control. I cannot change the HTML nor can I use JavaScript. I can only rely on good old CSS. The HTML is dynamic and look like this:

<div class="container">
    <div class="item" style="display: none">m</div>
    <div class="item">n</div>
    <div class="item">o</div>
    <div class="item">p</div>
</div>

I dont know how many children .container have. No nth-child() as far as I am aware of. The following example is valid too:

<div class="container">
    <div class="item" style="display: none">l</div>
    <div class="item" style="display: none">m</div>
    <div class="item">n</div>
    <div class="item">o</div>
    <div class="item">p</div>
</div>

I want to apply a specific property to the first visible element. Here it is 'n'.

If all element are red:

.item {
  background: red;
}

I would like to change it to blue:

??? {
  background: blue;
}

I tried:

.item {
  background: red;
}

.item:not([style='display: none']):first-child {
  background: blue;
}

Demo:

.item {
  background: red;
}

.item:not([style='display: none']):first-child {
  background: blue;
}
    <div class="container">
        <div class="item" style="display: none">m</div>
        <div class="item">n</div>
        <div class="item">o</div>
        <div class="item">p</div>
    </div>

Here 'n' should have a blue background.

How can I achieve that? What selector should I use?

aloisdg
  • 22,270
  • 6
  • 85
  • 105

1 Answers1

0

tl;dr (explanation followings):

.item[style='display: none'] + .item:not([style='display: none']) {
  background: blue;
}

Alright, first we can achieve this by rethinking the problem. Asking for the first visible child is actually the same logic as asking the first child after the last hidden child.

CSS have some uncommon combinator. Here we are going to use the adjacent sibling combinator (+). Let the MSDN introduce it:

The adjacent sibling combinator (+) separates two selectors and matches the second element only if it immediately follows the first element, and both are children of the same parent element.

/* Paragraphs that come immediately after any image */
img + p {
  font-weight: bold;
}

We can color every hidden .item following another hidden .item like that:

.item[style='display: none'] + .item[style='display: none'] {
  background: blue;
}

But what we want is the first child after the last hidden child. Here the pseudo-attribute :not() will do the trick:

.item[style='display: none'] + .item:not([style='display: none']) {
  background: blue;
}

Demo:

.item {
  background: red;
}

.item[style='display: none'] + .item:not([style='display: none']) {
  background: blue;
}
<div class="container">
    <div class="item" style="display: none">l</div>
    <div class="item" style="display: none">m</div>
    <div class="item">n</div>
    <div class="item">o</div>
    <div class="item">p</div>
</div>
aloisdg
  • 22,270
  • 6
  • 85
  • 105