232

I have a small issue trying to keep my .html pages at a consistent width on Chrome.

For example, I have a page (1) with lots of contents that overflows the viewport's (right word?) height, so there's a vertical scroll-bar on that page (1). On page (2), I have the same layout (menus, divs,...etc) but less content, so no vertical scroll-bars in there.

The problem is that on page (1) the scroll-bars seem to push elements slightly to the left (adding-up to the width?) while everything appears well centered on page (2).

I'm still a beginner on HTML/CSS/JS, and I'm fairly convinced that this isn't so difficult, but I had no luck figuring out the solution. It does work as intended on IE10, and FireFox (non-interfering scroll-bars), I only encountered this on Chrome.

Pang
  • 9,564
  • 146
  • 81
  • 122
Acemad
  • 3,241
  • 3
  • 23
  • 29
  • 1
    This is a very anoying issue, that i've never ran into until I switched to coding in Windows where scrollbars are persitent and take up screen realestate. Mac scrollbars disapear and don't add to the width. – Fydo Apr 13 '21 at 20:05
  • `scrollbar-gutter: stable both-edges;` as specified in https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter "If no scrollbar is present, the gutter will be painted as an extension of the padding" – daniel p Dec 09 '22 at 14:42
  • Doesn't work for me on Chrome. If vertical scrollbar appears, can cause the horizontal scrollbar to appear. – David Spector Aug 16 '23 at 10:24

20 Answers20

173

DISCLAIMER: overlay has been deprecated.
You can still use this if you absolutely have to, but try not to.

This only works on WebKit browsers, but I like it a lot. Will behave like auto on other browsers.

.yourContent{
   overflow-y: overlay;
}

This will make the scrollbar appear only as an overlay, thus not affecting the width of your element!

simonDos
  • 1,863
  • 2
  • 8
  • 9
77

All you need to do is add:

html {
    overflow-y: scroll;
}

in your css file, as this will have the scroller whether it is needed or not, though you just won't be able to scroll.

This means that the viewport will have the same width for both.

Pang
  • 9,564
  • 146
  • 81
  • 122
Hive7
  • 3,599
  • 2
  • 22
  • 38
  • 11
    so, i'm obliged to add scroll-bars to both ? isn't there a way to just ignore the width of the vertical scroll-bar ? Thank you ! – Acemad Aug 31 '13 at 13:13
  • Therer is no way to ignore it that I am aware of but what I said works a treat @Rockr90 – Hive7 Aug 31 '13 at 13:13
  • @d3c0y this is true and I have mentioned it in the answer but its the easiest method to do so. I do not know if that has changed in the past 3 years however – Hive7 Aug 15 '16 at 10:45
  • 1
    @aks. it forces the browser to think you can scroll so enables to scrollbar and scroll y is the side scroller – Hive7 Nov 13 '16 at 13:59
  • Doesn't do anything useful in Firefox – Mehdi Dehghani Jul 19 '18 at 15:38
  • 2
    overflow-y: scroll; will add/show the scrollbar in all viewport even there are no scrollable elements. – Abhilash KK Nov 06 '18 at 09:33
  • It is true that this solves scrollbar problems, but why doesn't the CSS team come up with a better way? I'm having hours of trouble trying to make scrollbars work consistently, just on Chrome alone. – David Spector Aug 16 '23 at 10:27
73

Probably

html {
    width: 100vw;
}

is just what you want.

Pang
  • 9,564
  • 146
  • 81
  • 122
Neurotransmitter
  • 6,289
  • 2
  • 51
  • 38
  • 1
    This worked great for my usecase: https://gist.github.com/bmcminn/1ab50da18bde75f39bc88e8292a5a847 I'm using `calc()` to determine the width of the main content view so that the fixed offscreen nav can occupy the left side of the screen on desktop, all while preserving native scroll on mobile. – bmcminn Dec 28 '17 at 22:23
  • 5
    I have been messing around with my scrollbars for the past 36 hours. I have had a few different solutions that worked, but raised other issues. This is the first solution that both works and allows me to use a normal scrollbar elsewhere. Thank you. – kosher Feb 19 '18 at 21:02
  • You're welcome! But messing around with `html`'s width in `vw`s is a bit _hacky_, so please exercise some caution! – Neurotransmitter Feb 20 '18 at 11:38
  • 1
    This worked great, can someone explain how this works ? – Zeus Dec 12 '18 at 19:26
  • 1
    Well, it just sets the root HTML node's (``) width to 100 _viewport width units_. That is 100% width of the browser window. – Neurotransmitter Dec 13 '18 at 08:31
  • 3
    Solution not bad, but it could add horizontal scrolling. – FDisk Mar 15 '19 at 14:03
  • @FDisk I only can think of added horizontal scrolling, if some elements exceed the width of the container (`` in this example). Think about `body { width: 120%; }`: this will increase the width of not only the ``, but also `` (since it's its container) and the horizontal scrollbar will be shown (or not, if you're on macOS and has it disabled, for example). – Neurotransmitter Nov 12 '19 at 10:56
  • 1
    Beautiful, simple solution – rkok Nov 30 '20 at 07:50
  • 1
    This is great solution, just very helpful also to add `overflow-x: hidden;` there ! – rlf89 Mar 15 '23 at 05:19
  • this solutions adds a horizontal scroll bar when there is a vertical scroll bar – Andrea D_ Mar 30 '23 at 14:10
  • No, it's some element deeper in the hierarchy which expands your over `100vw`. – Neurotransmitter Apr 24 '23 at 00:05
60

The 2021 solution is to use scrollbar-gutter, which adds the space, a scrollbar would use, permanently to an element.

Use

.element-class {
   scrollbar-gutter: stable both-edges;
}

both-edges is optional.

See also https://caniuse.com/mdn-css_properties_scrollbar-gutter and https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter

overflow: overlay is deprecated.

Here is a codepen for clarification on the different possibilities: https://codepen.io/waxolunist/pen/ExweBMz

Christian
  • 3,503
  • 1
  • 26
  • 47
  • 5
    Very nice, finally a better solution than all these hacks. `both-edges` caused a padding on the left of my textarea. So I just justed `scrollbar-gutter: stable;` – Avatar May 28 '22 at 18:44
  • nice didn't know this was possible, thanks for sharing ^^ – RolandMakkelie Sep 13 '22 at 12:03
  • This is the best thing that's happened to me today lol. Of course the fact that Safari has removed support (due to a bug) makes it a little less lovely. Fingers crossed this will be re-added to Safari too in the future. – aderchox Feb 16 '23 at 22:59
  • This one worked for me so far in Chrome. Works in Ms Edge too. Not so sure about Safari, but so far so good. Also works on Responsively App that uses Chromium as its browser engine. – Cosmas Ochieng Mar 04 '23 at 08:56
  • Seems not to work with Firefox yet. – Avatar Jun 28 '23 at 12:01
  • Actually it works fine for me with FF on Windows. Which OS are u using? – Christian Jun 28 '23 at 14:18
51

You can get the scrollbar size and then apply a margin to the container.

Something like this:

var checkScrollBars = function(){
    var b = $('body');
    var normalw = 0;
    var scrollw = 0;
    if(b.prop('scrollHeight')>b.height()){
        normalw = window.innerWidth;
        scrollw = normalw - b.width();
        $('#container').css({marginRight:'-'+scrollw+'px'});
    }
}

CSS for remove the h-scrollbar:

body{
    overflow-x:hidden;
}

Try to take a look at this: http://jsfiddle.net/NQAzt/

Lwyrn
  • 1,821
  • 1
  • 16
  • 27
  • 5
    The scrollHeight is the total height of an element. What you see and what is hidden. An example: Your window is like 500px in height, the body have a content for 900px. 500px are shown but the other 400px is hidden and the scroll bar will appear to show this remaning pixel. So the condition is like "if the height of all content is greater then the viewpoint height, add the margin to the body". Sorry for my bad english – Lwyrn Sep 02 '13 at 08:04
  • I got it now, thanks for the clear explanation, I tried this solution and got an additional horizontal scroll-bar, don't know why though. – Acemad Sep 02 '13 at 13:15
  • 2
    I've added a little piece of css to prevent it in the reply :) – Lwyrn Sep 02 '13 at 18:29
  • 4
    This solution disables scrolling – avalanche1 May 15 '16 at 16:52
  • 2
    Man you save me with this piece of code, I've lost 20 hours trying to figure out what should i do! Thanks buddy! –  May 26 '16 at 14:54
27

Webkit browsers like Safari and Chrome subtract the scroll bar width from the visible page width when calculating width: 100% or 100vw. More at DM Rutherford's Scrolling and Page Width.

Try using overflow-y: overlay instead.

Kyle Dumovic
  • 2,286
  • 1
  • 14
  • 7
  • 13
    Please don't post identical answers to multiple questions. Post one good answer, then vote/flag to close the other questions as duplicates. If the question is not a duplicate, *tailor your answers to the question.* – Paul Roub Sep 28 '16 at 23:52
  • 1
    This should work, but overlay is not yet standard neither it is supported in all browsers. – Zia Ul Rehman Mughal Dec 15 '16 at 07:52
  • Quick hiccup: in Safari this caused the page to stop scrolling – joshfindit Sep 04 '19 at 17:12
14

I found I could add

::-webkit-scrollbar { 
display: none; 
}

directly to my css and it would make the scrollbar invisible, but still allow me to scroll (on Chrome at least). Good for when you don't want a distracting scrollbar on your page!

fuzzy_logic_77
  • 387
  • 1
  • 4
  • 14
  • 5
    It is somewhat risky and not cross-browser solution though http://stackoverflow.com/questions/9251354/css-customized-scroll-bar-in-div – Alex Pyzhianov Jun 24 '16 at 16:19
6

For containers with a fixed width a pure CSS cross browser solution can be accomplished by wrapping the container into another div and applying the same width to both divs.

#outer {
  overflow-y: auto;
  overflow-x: hidden;
  /*
   * width must be an absolute value otherwise the inner divs width must be set via
   * javascript to the computed width of the parent container
   */
  width: 200px;
}

#inner {
  width: inherit;
}

Click here to see an example on Codepen

Robbendebiene
  • 4,215
  • 3
  • 28
  • 35
6
body {
    width: calc( 100% );
    max-width: calc( 100vw - 1em );
}

works with default scroll bars as well. could add:

overflow-x: hidden;

to ensure horizontal scroll bars remain hidden for the parent frame. unless this is desired from your clients.

CSS FTW
  • 61
  • 1
  • 1
3

It doesn't seem my other answer is working quite right at the moment (but I'll continue to try to get it operational).

But basically what you'll need to do, and what it was trying to do dynamically, is set the contents' width to slightly less than that of the parent, scrollable pane.
So that when the scrollbar appears it has no affect on the content.

This EXAMPLE shows a more hacky way of attaining that goal, by hardcoding widths instead of trying to get the browser to do it for us via padding.
If this is feasible this is the most simplest solution if you don't want a permanent scrollbar.

Hashbrown
  • 12,091
  • 8
  • 72
  • 95
  • That's a good solution, however i'm working on a responsive website using Bootstrap, and it's important to automate width and all, what do you say ? – Acemad Aug 31 '13 at 20:15
  • well unless (in order of decending likelyhood) either: someone figures out how to get my other answer to work (with either padding or margin); you can somehow use [media queries](http://mobile.smashingmagazine.com/2010/07/19/how-to-use-css3-media-queries-to-create-a-mobile-version-of-your-website/) to apply padding selectively; or [css expressions](http://stackoverflow.com/questions/6191679/css-expressions) are implemented in modern browsers to selectively apply padding. I think only a permanent scrollbar or using JS to detect scrollbar is the way to go – Hashbrown Sep 01 '13 at 05:15
1

EDIT: this answer isn't quite right at the moment, refer to my other answer to see what I was attempting to do here. I'm trying to fix it up, but if you can offer assistance do so in the comments, thanks!

Using padding-right will mitigate the sudden appearance of a scrollbar

EXAMPLE

chrome devtools showing padding unmoved

As you can see from the dots, the text makes it to the same point in the page before wrapping, regardless of whether or not a scrollbar is present.
This is because when a scrollbar is introduced the padding hides behind it, so the scrollbar doesn't push on the text!

Hashbrown
  • 12,091
  • 8
  • 72
  • 95
0
.modal-dialog {
   position: absolute;
   left: calc(50vw - 300px);
}

where 300 px is a half of my dialog window width.

This is actually the only thing that worked for me.

0

I had the same issue on Chrome. It only happened for me when the scroll bar is always visible. (found in the general settings) I have a modal and when I open the modal I noticed that...

body {
    padding-left: 15px;
}

is added to the body. To stop this from happening I added

body {
    padding-left: 0px !important;
}
NatD
  • 43
  • 1
  • 4
0

I can't add comment for the first answer and it's been a long time... but that demo has a problem:

if(b.prop('scrollHeight')>b.height()){
    normalw = window.innerWidth;
    scrollw = normalw - b.width();
    $('#container').css({marginRight:'-'+scrollw+'px'});
}

b.prop('scrollHeight') always equals b.height(),

I think it should be like this:

if(b.prop('scrollHeight')>window.innerHeight) ...

At last I recommend a method:

html {
 overflow-y: scroll;
}

:root {
  overflow-y: auto;
  overflow-x: hidden;
}

:root body {
  position: absolute;
}

body {
 width: 100vw;
 overflow: hidden;
}
jeremy
  • 43
  • 6
0

I solved a similar problem I had with scrollbar this way:

First disable vertical scrollbar by setting it's:

overflow-y: hidden;

Then make a div with fixed position with a height equal to the screen height and make it's width thin to look like scrollbar. This div should be vertically scroll-able. Now inside this div make another div with the height of your document (with all it's contents). Now all you need to do is to add an onScroll function to the container div and scroll body as the div scrolls. Here's the code:

HTML:

<div onscroll="OnScroll(this);" style="width:18px; height:100%;  overflow-y: auto; position: fixed; top: 0; right: 0;">
    <div id="ScrollDiv" style="width:28px; height:100%; overflow-y: auto;">
    </div>
</div>

Then in your page load event add this:

JS:

$( document ).ready(function() {
    var body = document.body;
    var html = document.documentElement;
    var height = Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
    document.getElementById('ScrollDiv').style.height = height + 'px'; 
});

function OnScroll(Div) {
    document.body.scrollTop = Div.scrollTop;
}

Now scrolling the div works just like scrolling the body while body has no scrollbar.

Arash Esmaeili
  • 179
  • 2
  • 6
  • Ok, and for browsers that show classic scroll bars, how would a user know there's more content to scroll to ? – Kalnode Jun 29 '21 at 17:13
0

Ran into the same issue of an extra width of a scrollbar on Chrome and Edge (not on a FireFox).

The logic in a project was to stop scrolling after hover on some container. So I added a class to a body:

    body.no-scrolling {
    overflow: hidden;
    }

That's helped to prevent scrolling, but removed a scrollbar. In a FireFox it wasn't a problem, but in Chrome and Edge content was filling the space of a scrollbar and made content of a page to move right.

So a solution that worked for me:

    html{
    width: 100vw;
    }

This rule prevented the content from moving right after scrollbar disappeared, but added an extra width and appeared a scrollbar in a bottom of a page. To hide that extra width I added:

     body{
     overflow-x: hidden;
     }

Let me know if it helped you too.

Double-w-B
  • 54
  • 1
  • 4
0

I had a similar issue with my Header nav. Most of my pages had enough content to trigger scroll-y, except one page.

My header nav has a flexContainer with 2 children (spaced-between). The flexContainer has a 5rem left & right padding.

header { padding-left: calc(100vw - 100%;)}

Adding this to the header centers the content by adding the scrollbar width to the left. The elements are now centered but we have effectively shrunk the header width by scrollbar width.

flexContainer {padding: 0rem calc(5rem - calc((100vw - 100%)/2));}

Im adding back the scrollbar width by reducing the flexContainer's padding.

This ensures that my content stays in place, regardless of scroll-y being triggered or not.

Merryl
  • 11
  • 1
0

I had a similar issue. I was trying to have scroll on hover but it kept pushing things off and making an awful UX. I found an easy solution to it. I keep the scrollbar in its place, but make it transparent and bring it back when the cursor hovers on the div. Since the scrollbar is always there it doesn't modify the design of the other elements of the div.

$(document).ready(function() {
$('#list-parent').hover(

    function() {
        $("#list-parent").removeClass('scrollbar-invisible');
                $("#list-parent").addClass('scrollbar');

    }, function() {
        $("#list-parent").addClass('scrollbar-invisible');
                $("#list-parent").removeClass('scrollbar');
    });
})
.main-div {
  height: 100px;
  width: 400px;
  overflow: hidden;
}

.list-parent {
  height: 100px;
  background-color: yellow;
  overflow: scroll;
  padding:20px;
  width:150px;
}

 .scrollbar-invisible::-webkit-scrollbar{
    border: 3px solid #ffffff00;
    width: 5px;
    height: 20px;
  }
  
  .scrollbar::-webkit-scrollbar {
    width: 5px;
    height: 20px;
  }


  .scrollbar::-webkit-scrollbar-thumb {
    border-radius: 100vh;
    border: 3px solid black;
    padding-left: 20px;
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="app" class='main-div'>
  <div id ='list-parent' class='list-parent scrollbar-invisible'>
    <div style='background-color:pink'>
      <h1> Todos: </h1>
      <ol>
        <li>Test Item </li>
        <li>Test Item </li>
        <li>Test Item </li>
        <li>Test Item </li>
        <li>Test Item </li>
        <li>Test Item </li>
        <li>Test Item </li>
      </ol>
    </div>
  </div>
</div>
doomsday
  • 141
  • 2
  • 12
0

scrollbar-gutter doesn't support old browser

so here is js solution support old browser

set boxSizing in scrollView style box-sizing: 'border-box" doesn't work

but when scrollView is rendered, js scrollView.style.boxSizing = "border-box", make rendering like scroll-gutter:stable

chikadance
  • 3,591
  • 4
  • 41
  • 73
-1

If you have some container that you're using as a wrapper over your website content, like for navigation and footer for example, you can add the following:

min-height: 101vh;

This will 'Lengthen' your page by 1vh to make sure the scrollbar never disappears and the width stays the same.

Sargsian
  • 9
  • 4