JavaScript: display: none and CSS computed heights

JavaScript: display: none and CSS computed heights

In this article we will see what happens to the layout model of HTML elements when you use this particular CSS declaration

In this article we will see what happens to the layout model of HTML elements when you use this particular CSS declaration.

The CSS display property allows us to specify a layout model for HTML elements. When you declare a layout model, browsers internally select a specific algorithm that will be applied to the final rendering of HTML elements.

When we declare display: none, we're actually performing an hard reset of the default layout model of a given element. The most important consequence of this from a pure JavaScript point of view is that computed dimensions are completely zeroed, especially heights.

Suppose that we want to show a modal overlay on a page. We can create the following HTML structure.

<html>
<body>
    <main id="site"></main>
    <div id="overlay"></div>
</body>
</html>

Now when we show the overlay we want to hide the main site's container with display: none. Our overlay should have an height that is equal to the overall height of the viewport. We can write the following CSS code:

#overlay {
    width: 100%;
    min-height: 100vh;
    position: absoute;
    top: 0;
    left: 0;
    z-index: 100000;
    background-color: #fff;
    display: none;
}

This is the simplest scenario that we can handle. When the overlay switches from none to block, browsers see the min-height: 100vh declaration, which is applicable to a block layout model, and automatically adjust the element's computed height to the desired value. So far so good.

But what happens to #site? Simply put, now its computed height is zero. In this case this is not a problem, because we don't need to take it into account when we want to display our overlay element.

The question is: what is the actual computed height of the document object when we apply display: none to an element? The answer is simple: it's given by the computed height of all elements that still have a display value othen than none minus the original computed height of all elements that now have the declaration display: none.

In our example, the computed height of the document object is zero. As we said earlier, this is not a problem because our overlay element has a declaration of min-height: 100vh and there are no more top level elements on that page.

Problems arise when our overlay element must have a dynamic height calculated via JavaScript. In this case we can't simply assign a static minimum height but we actually need to specify an height that equals the original document computed height.

The solution is to save the original computed height before our hide/show logic (see the MDN reference page).

'use strict';

const originalHeight = document.body.clientHeight;
const overlay = document.querySelector('#overlay');
const site = document.querySelector('#site');

site.style.display = 'none';
overlay.style.minHeight = originalHeight + 'px';
overlay.style.display = 'block';

Two more questions:

  1. Why did we choose absolute instead of fixed?
  2. Why did we choose min-height instead of height?

The answer is simple: the overflow property. When we have some predetermined content, such as a single image or video with fixed dimensions, it doesn't matter which positiong or height algorithm you choose.

But when you have some dynamic content with variable heights, such as a list of blog posts, choosing a fixed height algorithm will force the browser to decide whether to show or not a scrolling mechanism. This is particularly evident on mobile devices, when you'll get the unwanted side effect of having a portion of your contents which is not visible due to the fact that the page scrolling stops at the very bottom of the document.