In Web sites that use centered layouts, like mine, the page content seems to jump from left to right when navigating between some pages, or expanding content on a page. This happens because some pages are taller than other, causing the vertical scrollbar be present only in some cases. Because the vertical scrollbar takes some space on the page, the centered layout is shifted a few pixels. The screenshots below illustrate this issue. The top one is from a page with no scrollbar. The bottom one is from the same site but has a scrollbar. Notice that, although the content is centered on both pages, the bottom content is shifted to the left relative to the top content.

NoScroll

Scroll

I don’t like this behavior and will show a method for fixing it.

After some research, I found that the usual fix is to ensure that the vertical scrollbar is always visible. This is done using the overflow-y css property. While this prevents the jumping content, the downside is that the vertical scrollbar is always visible, even when there is no content to scroll to. I think we can do better.

My approach is to use javascript to reposition the content of the page while taking into account the visibility of the scrollbar. For that, we need to know if the scrollbar is visible and its width. If it is visible, we can adjust the position of the content. Note: I am using jQuery, but it is easy to convert the code to pure javascript if needed.

There is no direct way of knowing the size of the scrollbar and whether it is visible. The trick is to disable the scrollbar using overflow:hidden, measure the width of the body element, re-enable the scrollbar using overflow:auto and measure the width of the body element again. The difference between the measured widths is the width of the scrollbar. If the difference is zero, then the scrollbar is not visible at that moment.

// This script fixes the shift that occurs in a centered layout
// when the page grows and forces scrollbars to appear.
$(function () {
    var body = $("body");
    
    var previousWidth = null;
    
    // Function that applies padding to the body
    // to adjust its position.
    var resizeBody = function () {
        var currentWidth = body.width();
        if (currentWidth != previousWidth) {
            previousWidth = currentWidth;
    
            // Measure the scrollbar size
            body.css("overflow", "hidden");
            var scrollBarWidth = body.width() - currentWidth;
            body.css("overflow", "auto");
    
            body.css("margin-left", scrollBarWidth + "px");
        }
    };
    
    // setInterval is required because the resize event
    // is not fired when a scrollbar appears or disappears.
    setInterval(resizeBody, 100);
    resizeBody();
});