38

My page layout looks something like this:

<style type="text/css">
#content-wrap
{
    margin: 0 auto;
    width: 800px;
}
</style>

<div id="content-wrap">
</div>

You'll notice that the content-wrap div shifts its position a tad bit when the vertical scrollbar appears. One scenario is when the browser starts to progressively render the page without displaying the vertical scrollbar, then determines that a scrollbar is needed because the content is taller than the "fold". This shifts the div about 10px towards left.

What is the best way to tackle this problem without forcing the browser to always display the scrollbar?

Salman A
  • 262,204
  • 82
  • 430
  • 521
  • why do you need the horizontal scrollbar if the content is centered? – yoda Feb 18 '12 at 13:17
  • 1
    There is no horizontal scroll-bar, do you mean the vertical scroll-bar? – Christofer Eliasson Feb 18 '12 at 13:19
  • 1
    Yes, vertical scrollbar, thanks for correction. – Salman A Feb 18 '12 at 13:20
  • When the scroll bar appears your browser size in width becomes smaller, it will happen on even large websites. You can set a `min-height:` property to be large enough to always show the scroll bar – Anagio Feb 18 '12 at 13:25
  • 2
    The best way to tackle this problem is to force the browser to always display the vertical scrollbar. **Why don't you want to do that?** `html { overflow-y: scroll; }` does this with no downsides except a few small [edge cases](https://github.com/h5bp/html5-boilerplate/issues/918). – thirtydot Feb 18 '12 at 13:28
  • Possible duplicate of [How Do I Stop My Web Content From Shifting Left When The Vertical Scrollbar Appears? Roll-Up of Advice 2017](https://stackoverflow.com/questions/45524214/how-do-i-stop-my-web-content-from-shifting-left-when-the-vertical-scrollbar-appe) – JBH Aug 05 '17 at 17:17

7 Answers7

39

I'm afraid the best way to solve this is to force the scroll bar to be visible at all times with html {overflow-y: scroll;}. The problem you have is that the "available area" shrinks with say 10 px when the scroll bar appear. This cause the calculated margin on your left side to shrink with half the width of the scroll bar, thus shifting the centered content somewhat to the left.

A possible solution might be to calculate the margin with JavaScript instead of using margin: 0 auto; and somehow compensate for the "lost" pixels when the scroll bar appear, but I'm afraid it will be messy and the content will probably move a little bit anyway while you calculate and apply the new margin.

Christofer Eliasson
  • 32,939
  • 7
  • 74
  • 103
  • 1
    Overflow-y:scroll is the without a doubt the best way to do this today. When/if other OSes copy Apple's iOS-style floating scrollbars, this issue will cease to exist. – Chad von Nau Feb 18 '12 at 13:36
22

If your site is "responsive" (reacts to width):

Step 1: Add width: 100vw to a wrapper element. This makes it as wide as the viewport, ignoring the appearance of a scrollbar.

Step 2: Add overflow-x: hidden to the content element. This will remove the horizontal scrollbar (created when vertical scrollbar appears, to allow the user to "look under" it).

"wrapper element" is in our case referring to another div around your #content-wrap

Will work for your case too, tested:

<style type="text/css">
body {
    overflow-x: hidden;
}
#wrap-wrap {
    width: 100vw;
}
#content-wrap
{
    margin: 0 auto;
    width: 800px;
}
</style>
<div id="wrap-wrap">
    <div id="content-wrap">
    </div>
</div>

Make sure nothing useful on your page is wide enough to get caught under the scrollbar.

For example, you can ensure that the sum of (horizontal padding + border + horizontal margin) of the content element is wider than the scrollbar).


If your site is fixed width + centered (your case):

html {
    margin-left: calc(100vw - 100%);
    margin-right: 0;
}

This will add a left margin equal in width to the scrollbar when it appears. Which is 0 when it does not. Taken from here, but tested.

Mihail Malostanidis
  • 2,686
  • 3
  • 22
  • 38
6

You must use:

html {
  overflow-y: overlay;
}

Only supported by WebKit (Safari) or Blink (Chrome, Opera)

A. Morel
  • 9,210
  • 4
  • 56
  • 45
3

Use jquery and put this in the start of your tag:

<script type="text/javascript">
function checkheight(){
 if ($(document).height() > $(window).height()) {
 //that is if there is vertical scrollbar
 document.getElementById('yourcenteredcontainer').style.paddingLeft='8px'; 
 //8px because the scrollbars are (?always?) 16px
 }else{
 document.getElementById('yourcenteredcontainer').style.paddingLeft='0px';
 }
}
</script>

and call the function checkheight(); in the end of your tag plus wherever you have onclick (or other) events that make the page longer or shorter in height.

Fanky
  • 1,673
  • 1
  • 18
  • 20
0

First I would recommend optimizing the HTML so that it won't take so long to load/render. If load/render is fast the scrollbar won't appear "too late". What is it that takes long to load/render? Check the network tab in chrome debug tools (F12). Do an audit in Chrome debug tools.

There are multiple things that could make the document "reflow", and the scrollbar appear even though the browser could have known the necessary measurements right from the start. Are you using tables for layout - don't! They may need multiple passes of rendering. Do you have images without width/height specified? Then the document will need to be rerendered when each image loads. Specify <img ... style="width: ..px; height: ..px">. Is the CSS sane and efficient?

If you can't get load/rendering speed down I think your best bet is to not use the browser's scrollbar if javascript is enabled. That way you can control it and place it absolutely positioned so that it won't affect horizontal positioning.

Let your slider start of with display: none. Monitor dom ready event as well as image load events as well as window resize events. When the page has been loaded, images have been loaded and when window gets resized just run the same function every time. It would determine if the scrollbar is needed and either display it or hide it.

You could use JQuery UI Slider for example and set it's maxValue to $(document).height() - $(window).height(), monitor the slider change event and then scroll the body to the value of the slider and so forth.

If javascript is disabled the fallback will be the regular scrollbar and there's nothing you can do about the slight horizontal shift then.

But really I think the problem of the horizontal shift is too small to spend time fixing with a custom scrollbar, and check that it actually works well on all platforms etc. Do HTML/CSS optimizations first.

oldwizard
  • 5,012
  • 2
  • 31
  • 32
0

You can try this solution: https://stackoverflow.com/a/67213174/14302216

But the widths can't be relative. Probably, width:100vw will work for the parent, but I'm not sure how you would set the child width. I'm afraid calc(100vw-16px) will not work. But if you can set like widht:800px for the child, it will be fine!

0

If you can use Javascript, you can set the width of the content-wrap to the inner width of the window minus the standard width of a scrollbar.

You will run into some problems though.

  • The user will have to have Javascript enabled
  • You don't know what the width of the vertical scrollbar is, especially if the scrollbar isn't there! So you will have to guess. 20px seems like a good guess.
  • Different browsers have different ways of telling you want the inner width of the window is.

So if you can live with all that, you can do something like this (in pseudo code)

if window.innerWidth is defined :
   set the width of the div to window.innerWidth-20px
else if we're running on Internet Explorer :
   set the width to document.documentElement.offsetWidth-20px
otherwise :
   we're out of luck and we best leave the width as is.
Mr Lister
  • 45,515
  • 15
  • 108
  • 150