jQuery Visible Selector with Visibility Hidden
This article explains how jQuery’s :visible selector
evaluates elements styled with CSS visibility: hidden.
While many developers expect this selector to check whether an element
can physically be seen on the screen, jQuery actually evaluates
visibility based on whether the element occupies layout space in the
document. Consequently, elements styled with
visibility: hidden are considered :visible by
jQuery, which often leads to unexpected behavior in front-end
scripts.
How jQuery Defines Visibility
The jQuery :visible selector does not inspect
human-perceptible visibility, such as transparency or visual
obscuration. Instead, jQuery considers an element visible if it consumes
space in the layout. Under the hood, jQuery determines an element's
visibility by verifying if its layout dimensions are greater than zero
or if it generates DOM client rects:
jQuery.expr.pseudos.visible = function( elem ) {
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};Because an element must consume space to be considered visible,
jQuery treats the following as :hidden:
- Elements with
display: none. - Form elements with
type="hidden". - Elements with both
width: 0andheight: 0. - Elements whose ancestors are hidden with
display: none.
The Behavior of
visibility: hidden
CSS visibility: hidden hides an element visually, but
unlike display: none, the element retains its position,
width, height, and box-model geometry in the document flow.
Because the element still has positive values for
offsetWidth, offsetHeight, and
getClientRects(), jQuery’s layout-based visibility check
passes. Therefore, running $('selector').is(':visible') on
an element with visibility: hidden returns
true.
Similarly, elements with opacity: 0 also occupy space
and will be evaluated as :visible.
How to Check for
visibility: hidden
To accurately detect if an element has its CSS visibility set to
hidden, you cannot rely on the :visible pseudo-selector.
Instead, inspect the computed CSS property directly:
// Check if an element has visibility: hidden
var isHidden = $('#myElement').css('visibility') === 'hidden';
// Combine both checks to find elements that are rendered and visible to the user
var isFullyVisible = $('#myElement').is(':visible') && $('#myElement').css('visibility') !== 'hidden';Alternatively, to filter a collection of elements that are both
layout-visible and not hidden via CSS visibility, you can use jQuery's
.filter() method:
var visibleElements = $('.items').filter(function() {
return $(this).is(':visible') && $(this).css('visibility') !== 'hidden';
});