jQuery and CSS display: none: an overview of problems and solutions

The most frequently asked question about jQuery effects is "Why this effect doesn't work?". Surprisingly, the most recurrent answer is simple: you didn't set up your CSS styles correctly. CSS styles affect jQuery effects in many ways. In this article I'll try to give you a comprehensive overview of the specific problem of CSS visibility with display: none applied to jQuery.

The CSS properties opacity, visibility and display work very differently from each other. When you change the opacity or the visibility of an element, this element is still present in the visual layout of the page.

Conversely, setting the display property of an element to none removes entirely the element from the visual layout of your document. This implies that the targeted element no longer affects other elements visually.

But there's a catch: jQuery is not able to compute correctly the offsets and positions of elements with display: none. When you use such a declaration, bear in mind that when the element will revert back to its normal state, the horizontal coordinates will be always computed to zero.

A common solution to this problem is to store the original position of an element just before making it disappear.

var $element = $( '#test' );
var origLeftOffset = $element.position().left;
$element.data( 'left', origLeftOffset );
$element.fadeOut( 'slow' );

As you can see, this solution applies to elements that are dynamically animated via jQuery. The real problem is when it comes to apply this solution to elements that are initially hidden via CSS rules.

If elements are positioned, then we can use the css() method to read their original property values:

var $element = $( '#test' );
var origLeftOffset = parseInt( $element.css( 'left' ) );

Unfortunately, this solution falls short when hidden elements are not positioned. Consider this example:

#test {
  width: 100px;
  height: 100px;
  margin: 20px;
  display: none;
}

We can either try a pure jQuery or JavaScript approach but the final result will be always the same:

var $element = $( '#test' );
var element = document.getElementById( 'test' );
console.log( $element.offset().top, $element.offset().left );
// 0 0
var rect = element.getBoundingClientRect();
console.log( rect.top, rect.right, rect.bottom, rect.left );
// 0 0 0 0

But if I change display: none to visibility: hidden, I get 20 28 from the jQuery properties and 20 128 120 28 from the JavaScript approach.

Another problem related to element's visibility is when you have to show and hide a dynamic content created via JavaScript. A typical example of this problem are Google maps.

Suppose that you have a Google map created by using the Google's APIs. This map is initially hidden. When a user clicks on a button, the map slides down. Then when the user clicks again on the same button, the map slides up.

If you use display: none, your map will render badly when shown with a jQuery method. More precisely, the map area won't cover the entire canvas.

Suppose that you have the following CSS code:

#map {
  width: 400px;
  height: 300px;
  margin: 20px 0;
  display: none;
}

The following video show what happens to your map when you try to show it by using one of the available jQuery effect methods.

Instead, if the map is initially visible and then you hide and show it dynamically, the rendering shows no flaws.

Back to top