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: 0 and height: 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';
});