22

Is there anyway to use a pseudo selector with Firefox's querySelector() or querySelectorAll() functions to detect visibility? In particular I want to be able to do something like this:

elem.querySelector('#list .list-item:visible');
elem.querySelector('#section .sub-section:visible .title');

No need to worry about browser inconsistencies or other implementation, just Firefox. Thanks!

EDIT: Visible is defined by display not being none and visibility not being hidden.

macguru2000
  • 2,042
  • 2
  • 18
  • 27

6 Answers6

23

Since there is no native implimentation of the :visible pseudo selector I decided to use CSS classes to hide and show my elements, thus allowing to simply just check for the class name instead of visibility. Here is what my selector looks like now:

elem.querySelector('#list .list-item:not(.hidden)');

Now in 2016 we can also use the hidden html5 attribute, so this selector would work too:

elem.querySelector('#list .list-item:not([hidden])');
macguru2000
  • 2,042
  • 2
  • 18
  • 27
  • 2
    is this still the best way in 2015? – SuperUberDuper Dec 29 '15 at 13:34
  • 1
    I believe so, :visible is not and will probably never be part of the css specification. – macguru2000 Dec 29 '15 at 15:17
  • 1
    A more popular way to do this in 2016 is to use the new hidden html5 global attribute. It is not fully backward compatible, so be careful. Here is Mozilla's docs https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden – macguru2000 Apr 13 '16 at 17:59
  • @SuperUberDuper the hidden attribute sets the elements display to none. So no, it does not consider opacity. – macguru2000 Jan 04 '18 at 18:52
15

For finding elements that are not display:none the CSS selector equivalent to :visible is

:not([style='display:none'])

You could do the same for visibility:hidden and then chain :not() selectors if you need to.

Here's a fiddle.

Edit: Be careful with spaces and other punctuation. If you later manipulate these elements with JQuery and hide(), for instance, and need to call the same function, then you will need to chain :not([style="display: none;"]) to your CSS selector.

iandllnghm
  • 301
  • 3
  • 8
12

No, there isn't. The CSS specification doesn't define a :visible (or related) selector, and AFAIK Firefox doesn't implement non-standard pseudo selectors.

If you'd like to implement this yourself, note how jQuery implements its :visible selector:

In jQuery 1.3.1 (and older) an element was visible if its CSS "display" was not "none", its CSS "visibility" was not "hidden", and its type (if it was an input) was not "hidden". In jQuery 1.3.2 an element is visible if its browser-reported offsetWidth or offsetHeight is greater than 0.

Source: http://docs.jquery.com/Release:jQuery_1.3.2#:visible.2F:hidden_Overhauled

David Wolever
  • 148,955
  • 89
  • 346
  • 502
  • That's what I feared...to bad. I understand it isn't a useful pseudo selector for CSS, but it would be really helpful in Javascript. – macguru2000 Nov 14 '12 at 22:51
  • the jQuery implementation doesn't really work well at all for detecting the css visibility property as `visibility: hidden` will take space on the document – Esailija Nov 14 '12 at 22:55
  • And on philosophical level perhaps that is the correct implementation...depends on what you consider visible...if it is taking space on the page then there is some room to argue that the element is "visible". – macguru2000 Nov 14 '12 at 23:17
7

Using plain javascript, you could also emulate jQuery behaviour easily, turning your querySelector results into an array, and filtering it:

Array.prototype.slice.call(document.querySelectorAll('your selector'))

That creates a plain array with the results of your selector, that you can filter as:

.filter(function (item,index) { return item.style.display!="none" } );

or even the rest of jquery conditions (item.style.visibility != "hidden" && item.style.opacity > 0 && ...).

As a one liner:

Array.prototype.slice.call(document.querySelectorAll('your selector')).filter(function (item,index) { return item.style.display!="none" } );
Javier Guerrero
  • 328
  • 4
  • 6
3

Checking if element is visible, supports on all major browsers:

jQuery:

$('.list-item').is(':visible');

Vanilla JS:

function isVisible(elem) {return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0; }
Tomer Gal
  • 933
  • 12
  • 21
  • 1
    Are you sure those two examples are equivalent in all versions of jQuery? – macguru2000 Apr 18 '16 at 17:20
  • 1
    @macguru2000 They are equivalent in the latest version of jQuery, I didn't check (and therefore never mentioned) earlier versions of jQuery regarding this method. – Tomer Gal Apr 19 '16 at 12:05
  • I see, please don't take this the wrong way, but when you've been doing this for over a decade you stop assuming a mention means the "latest version". Think about what someone else reading your answer in 2 years time would think, jQuery3? – macguru2000 Apr 19 '16 at 14:28
0

You can use the offsetParent value to determine if the element is visible or not.

let parent = document.getElementById('parent');
let allSubChildren = parent.querySelectorAll('.sub-children');

let visibleChildren = Array.prototype.slice.call(allSubChildren).filter(function (item, index) {
    console.log('Element "' + item.textContent + '" is ' + (item.offsetParent !== null ? 'visible' : 'hidden'));
    return item.offsetParent !== null;
  }
);

console.log(visibleChildren);
.none {
  display: none;
}

.hidden {
  visibility: hidden;
}
<p><b>item.offsetParent</b> returns <b>null</b> if the element or any parent is not displayed.</p>
<em>Check the console</em>

<div id="parent">
  <div class="children">
    <div class="sub-children">visible</div>
  </div>
  <div class="children hidden">
    <div class="sub-children">visibility hidden doesn't work</div>
  </div>
  <div class="children none">
    <div class="sub-children">display none</div>
  </div>
  <div class="children">
    <div class="sub-children">also visible</div>
  </div>
  <div class="children">
    <div class="sub-children none">also display none</div>
  </div>
</div>

<div id="result">

</div>
Tim
  • 1,238
  • 1
  • 14
  • 24