197

A tricky CSS selector question, don't know if it's even possible.

Lets say this is the HTML layout:

<div></div>
<div></div>  
<div></div>  
<div style="display:none"></div>
<div style="display:none"></div>  

I want to select the last div, which is displayed (ie. not display:none) which would be the third div in the given example. Mind you, the number of divs on the real page can differ (even the display:none ones).

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Vishal Shah
  • 3,774
  • 4
  • 23
  • 25
  • One of the problems with this request is that, as a general principle, CSS can select an element only based on previous elements (ancestors or previous siblings), and not on anything that comes afterwards. So your
    can't get selected based on any properties of
    s that come after it.
    – Doin Oct 25 '20 at 20:06

12 Answers12

82

You could select and style this with JavaScript or jQuery, but CSS alone can't do this.

For example, if you have jQuery implemented on the site, you could just do:

var last_visible_element = $('div:visible:last');

Although hopefully you'll have a class/ID wrapped around the divs you're selecting, in which case your code would look like:

var last_visible_element = $('#some-wrapper div:visible:last');
Eric Caron
  • 6,181
  • 1
  • 24
  • 26
Surreal Dreams
  • 26,055
  • 3
  • 46
  • 61
  • 28
    the question clearly says no "a JQuery CSS Selector", says "A CSS SELECTOR", this should be the accepted ANSWER =P – AgelessEssence May 18 '11 at 00:24
  • 2
    This is, indeed, the answer to the original question. The questioner never mentioned javascript or jquery and those tags were added by another answerer. 1nfected - do you actually want jQuery? If so, please actually put that in your question. Otherwise, you should accept this answer instead. – jep Oct 11 '13 at 15:11
  • I think jQuery is acceptable to the OP. That was the accepted solution, after all. – Surreal Dreams Oct 11 '13 at 15:59
  • 23
    Whhyyyy is the first answer allllwaaayyysss jquery? This is a _javascript_ answer to a _CSS_ question. CSS !== javascript – dudewad Apr 21 '17 at 18:12
  • 5
    @dudewad: See the last part of the first sentence: "but CSS alone can't do this." The answer could leave it at that, with no alternative in sight... or provide an alternative. But this answer has at least done its due diligence in actually stating that CSS cannot do what is being asked (albeit it doesn't elaborate *why*). On the other hand, an answer that just goes off on a JavaScript solution without acknowledging the CSS nature of the question *is* worth criticizing. – BoltClock Oct 24 '18 at 12:18
53

The real answer to this question is, you can't do it. Alternatives to CSS-only answers are not correct answers to this question, but if JS solutions are acceptable to you, then you should pick one of the JS or jQuery answers here. However, as I said above, the true, correct answer is that you cannot do this in CSS reliably unless you're willing to accept the :not operator with the [style*=display:none] and other such negated selectors, which only works on inline styles, and is an overall poor solution.

dudewad
  • 13,215
  • 6
  • 37
  • 46
  • Firstly, I agree with you but I do think that using the `:not` operator as you stated can work in certain circumstances. For example, this works perfectly for my situation where I have JS conditionally hiding elements which then adds inline styles and thus your solution actually works perfectly :) – doz87 Sep 01 '17 at 05:12
  • 2
    Well, then I guess what works works :) I'm just a bit of an idealist and I like to stress where things aren't the best, its important to be able to distinguish what is 'hacky' and what isn't because it makes us all better programmers. – dudewad Sep 01 '17 at 18:59
  • Yeah I hear ya, I agree that this solution shouldn't be used as a css only solution to the OP query, I do think that this works very well when used in conjunction with JS though. – doz87 Sep 02 '17 at 04:21
  • You might also use a CSS class to hide elements (instead of an inline style). If that's the case, you can just as well negate the selector for that hiding class. This always selects the same what is hidden because both are set by the same CSS class. So your answer goes in the right direction but not far enough. :-) – ygoe Nov 21 '19 at 09:09
  • Agree. If you are using a media query to hide some elements, no pure CSS solution for now. – AGamePlayer Jun 20 '22 at 08:58
17

Try

div:not([style*="display: none"]):last-child
TylerH
  • 20,799
  • 66
  • 75
  • 101
Tyler Wayne
  • 287
  • 4
  • 11
  • 2
    makes total sense the selector, but since you need to select the last child itself, it must not be display none and get only the last child, so :not([style*="display: none"]):last-child – Miguel Feb 23 '21 at 11:47
  • this is a perfect solution if you're planning on hiding some elements some of the times, e.g. using jQuery. – Lis May 24 '21 at 10:48
  • 2
    It does not select the last child – Zain Nov 09 '21 at 07:55
  • 6
    **This won't work,** because the two pseudo-selectors are independent filters that are combined _(not processed sequentially)_. It takes the intersection of `div:last-child` and `div:not(...)` and uses the result of this intersection. In other words, if the actual last child has `display: none` it won't be selected, but also, the div without this style won't be selected also if it's not the last child. – ADTC Jun 29 '22 at 09:51
14

If you can use inline styles, then you can do it purely with CSS.

I am using this for doing CSS on the next element when the previous one is visible:

div[style='display: block;'] + table {
  filter: blur(3px);
}
Alex Grande
  • 7,929
  • 1
  • 26
  • 30
9

I think it's not possible to select by a css value (display)

edit:

in my opinion, it would make sense to use a bit of jquery here:

$('#your_container > div:visible:last').addClass('last-visible-div');
Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
Guillaume86
  • 14,341
  • 4
  • 53
  • 53
6

Pure JS solution (eg. when you don't use jQuery or another framework to other things and don't want to download that just for this task):

var divs = document.getElementsByTagName('div');
var last;

if (divs) {
  for (var i = 0; i < divs.length; i++) {
    if (divs[i].style.display != 'none') {
      last = divs[i];
    }
  }
}

if (last) {
  last.style.background = 'red';
}
<div>A</div>
<div>B</div>
<div>C</div>
<div style="display:none">D</div>
<div style="display:none">E</div>

External link

Teocci
  • 7,189
  • 1
  • 50
  • 48
pavel
  • 26,538
  • 10
  • 45
  • 61
  • Thanks for the snippet, the only non-jQuery reply! However, this doesn't work when having multiple parent elements to apply to: http://jsfiddle.net/uEeaA/100/ - can you help me build a solution for others that works? I need it, others need it, so please help :) – mnsth Jul 25 '15 at 19:05
  • I realize this is an old thread, but for any future visitors, you could do something along the lines of http://jsfiddle.net/uEeaA/103/ – xec Feb 16 '16 at 08:08
  • I wanted to up vote this just b/c you dropped jQuery - good on you. My only complaint is a div can be hidden from view in many ways, not just based on the style parameter. Real quick: [1] width and height can be set to 0, [2] transform scale can be 0, [3] and we can place it out of the viewable area. – Markus May 11 '16 at 15:25
3

It is not possible with CSS, however you could do this with jQuery.

JSFIDDLE DEMO

jQuery:

$('li').not(':hidden').last().addClass("red");

HTML:

<ul>
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li class="hideme">Item 4</li>    
</ul>

CSS:

.hideme {
    display:none;
}

.red {
    color: red;
}

jQuery (previous solution):

var $items = $($("li").get().reverse());

$items.each(function() {

    if ($(this).css("display") != "none") {
        $(this).addClass("red");
        return false;
    }

});
alex
  • 479,566
  • 201
  • 878
  • 984
martynas
  • 12,120
  • 3
  • 55
  • 60
1

in other way, you can do it with javascript , in Jquery you can use something like:

$('div:visible').last()

*reedited

eveevans
  • 4,392
  • 2
  • 31
  • 38
1

Just for the sake of completeness, I solved this problem using pure css with the :has operator.

In your example you will have the following rule : div:has(+ div[style*="display:none"] It will select all node with the condition of having a direct sibling with a display:none style rule.

div > div:has(+ div[style*="display:none"]),
div > div:last-child {
  background: red;
  color: white;
}
<h3>Example 1 :</h3>
<div>
  <div>A</div>
  <div>B</div>  
  <div>C</div>  
  <div style="display:none">D</div>
  <div style="display:none">E</div> 
</div>
<h3>Example 2 :</h3>
<div>
  <div>A</div>
  <div>B</div>  
  <div>C</div>  
  <div>D</div>
  <div>E</div> 
</div>

div:last-child is only there to support the case where all the blocks would be visible, therefore there would be no sibbling but we want to apply the css rule anyway.

Please note that it only works because your hidden divs are sibblings.

Glaerferyn
  • 161
  • 7
0

div:not([style*="display:none"], :has(~ div:not([style*="display:none"])))

We need to get the last thing that matches div:not([style*="display:none"]). Let's call that x. We need to select x and then get the last one, which would be any x which does not have any x after it. To select something which has a selector after it, we use :has(~ ...) and we use :not(...) to negate it.

So x:not(:has(~ x)), or div:not([style*="display:none"]):not(:has(~ div:not([style*="display:none"])), simplified to div:not([style*="display:none"], :has(~ div:not([style*="display:none"]))

s456
  • 5
  • 4
-1

If you no longer need the hided elements, just use element.remove() instead of element.style.display = 'none';.

codedge
  • 4,754
  • 2
  • 22
  • 38
  • This removes the element from the DOM, it doesn't just hide it – maicol07 May 11 '22 at 13:52
  • Yes, of course. As i mentioned, it is useful for the cases where you no longer need the hided elements. It reminds solving the problem by questioning its necessity and reducing it into a simple and more clean job. – Masoud Shariati May 15 '22 at 09:31
-10

This worked for me.

.alert:not(:first-child){
    margin: 30px;
}
kmukku
  • 76
  • 1
  • 7