75

If an element is set to width: 100vw; and there is a vertical scrollbar the width of the element will be equal to the viewport plus the width of the scrollbar.

Is it possible to prevent this?

Is it possible to prevent this without disabling horizontal scrolling on the entire page? Aside from changing my css/markup to make the element 100% of the body width I can't think of anything.

Tested in Chrome Version 43.0.2357.81 m & FF 36.0.1 & Opera 20.0.1387.91 on Windows 8.1

Here is the code as requested:

Example

html

<div class="parent">
    <div class="box"></div>
</div>
<div class="tall"></div>

css

body { margin: 0; }
html { box-sizing: border-box; }
*, *::before, *::after {
    box-sizing: inherit;
    position: relative;
}
.parent {
    background: rgba(0, 0, 0, .4);
    height: 100px;
    width: 5rem;
    margin-bottom: 25px;
}

.box {
    position: absolute;
    top: 0;
    left: 0;
    background: rgba(0, 0, 0, .4);
    height: 50px;
    width: 100vw;
}

.tall {
    height: 100rem;
}
hal
  • 4,845
  • 7
  • 34
  • 57

13 Answers13

46

Basically the answer is no, if you have a vertical scrollbar there is no way to make 100vw equal the width of the visible viewport. Here are the solutions that I have found for this issue.

warning: I have not tested these solutions for browser support


tl;dr

If you need an element to be 100% width of the visible viewport(viewport minus scrollbar) you will need to set it to 100% of the body. You can't do it with vw units if there is a vertical scrollbar.


1. Set all ancestor elements to position static

If you make sure that all of .box's ancestors are set to position: static; then set .box to width: 100%; so it will be 100% of the body's width. This is not always possible though. Sometimes you need one of the ancestors to be position: absolute; or position: relative;.

Example

2. Move the element outside of non-static ancestors

If you can't set the ancestor elements to position: static; you will need to move .box outside of them. This will allow you to set the element to 100% of the body width.

Example

3. Remove Vertical Scrollbar

If you don't need vertical scrolling you can just remove the vertical scrollbar by setting the <html> element to overflow-y: hidden;.

Example

4. Remove Horizontal Scrollbar This does not fix the problem, but may be suitable for some situations.

Setting the <html> element to overflow-y: scroll; overflow-x: hidden; will prevent the horizontal scrollbar from appearing, but the 100vw element will still overflow.

Example

Viewport-Percentage Lengths Spec

The viewport-percentage lengths are relative to the size of the initial containing block. When the height or width of the initial containing block is changed, they are scaled accordingly. However, when the value of overflow on the root element is auto, any scroll bars are assumed not to exist. Note that the initial containing block’s size is affected by the presence of scrollbars on the viewport.

It appears that there is a bug because vw units should only include the scrollbar width when overflow is set to auto on the root element. But I've tried setting the root element to overflow: scroll; and it did not change.

Example

Collin Anderson
  • 14,787
  • 6
  • 68
  • 57
hal
  • 4,845
  • 7
  • 34
  • 57
15

This is a more full-fledged approach to the bug since it still exists in modern browsers. Setting overflow-x: hidden can cause problems or be undesirable in many situations.

A full example is available here: http://codepen.io/bassplayer7/pen/egZKpm

My approach was to determine the width of the scroll bar and use calc() to reduce the 100vw by the amount of the scroll bar. This is a little more complicated because in my case, I was pulling the width of the content out from a box that had a defined with so I needed to declare the margin as well.

A few notes regarding the code below: first, I noticed that 20px seems to be a rather broad magic number for the scroll bars. I use a SCSS variable (it doesn't have to be SCSS) and code outside of @supports as a fallback.

Also, this does not guarantee that there will never be scroll bars. Since it requires Javascript, users that don't have that enabled will see horizontal scroll bars. You could work around that by setting overflow-x: hidden and then adding a class to override it when Javascript runs.

Full SCSS Code:

$scroll-bar: 20px;

:root {
    --scroll-bar: 8px;
}

.screen-width {
  width: 100vw;
  margin: 0 calc(-50vw + 50%);

    .has-scrollbar & {
        width: calc(100vw - #{$scroll-bar});
        margin: 0 calc(-50vw + 50% + #{$scroll-bar / 2});
    }

    @supports (color: var(--scroll-bar)) {
        .has-scrollbar & {
            width: calc(100vw - var(--scroll-bar));
            margin: 0 calc(-50vw + 50% + (var(--scroll-bar) / 2));
        }
    }
}

Convert the above to plain CSS just by removing #{$scroll-bar} references and replacing with the px value

Then this Javascript will set the CSS Custom Property:

function handleWindow() {
    var body = document.querySelector('body');

    if (window.innerWidth > body.clientWidth + 5) {
        body.classList.add('has-scrollbar');
        body.setAttribute('style', '--scroll-bar: ' + (window.innerWidth - body.clientWidth) + 'px');
    } else {
        body.classList.remove('has-scrollbar');
    }
}

handleWindow();

As a side note, Mac users can test this by going to System Preferences -> General -> Show Scroll Bars = Always

bassplayer7
  • 924
  • 1
  • 13
  • 38
9

Using max-width attribute with width:100vw and it solved my problem.

Here's what i used.

.full{
     height: 100vh;
     width: 100vw;
     max-width: 100%;
    }

Basically what it does is it fixes the max width to the viewport so the horizontal scroll gets eliminated.

More @ https://www.w3schools.com/cssref/pr_dim_max-width.asp

The max-width property defines the maximum width of an element.

If the content is larger than the maximum width, it will automatically change the height of the element.

If the content is smaller than the maximum width, the max-width property has no effect.

amarinediary
  • 4,930
  • 4
  • 27
  • 45
Animesht Tiwari
  • 115
  • 1
  • 1
4

Paddings and borders can interfere. So can margin. Use box-sizing to calculate width including these attributes. And maybe remove margin (if any) from the width.

* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
body {
    margin: 0; /* interferes with 100vw */
}
.parent {
    width: 100vw;
    max-width: 100%; /* see below */
}
.box {
    width: 100%; /* For those good old-fashioned browsers with no vw or calc() support */
    width: -webkit-calc(100vw - [your horizontal margin, if any]);
    width: -moz-calc(100vw - [your horizontal margin, if any]);
    width: calc(100vw - [your horizontal margin, if any]);
    max-width: 100%
}

It seems you have to add max-width: 100%; if there is a reflow which is causing the scrollbar to appear after the initial viewport width calculation. This does not seem to happen in browsers without an interfering scrollbar (iOS, OS X, IE 11 Metro), but can affect all other browsers.

Niklas Brunberg
  • 769
  • 7
  • 17
  • If you're going to use prefixes for box-sizing, you'll definitely need some for calc as well. – Bram Vanroy May 27 '15 at 18:06
  • @BramVanroy absolutely. -moz-calc() and -webkit-calc() are available. – Niklas Brunberg May 27 '15 at 18:10
  • (I suggest you add them to your example. ;-)) – Bram Vanroy May 27 '15 at 18:12
  • 2
    I don't understand how this solves the issue. I want the element to equal 100vw minus the scrollbar width, not 100vw minus it's parent's margins. Are you saying that vw adds margins to it's calculation? Because that doesn't sound right. – hal May 27 '15 at 18:26
  • @moss 100w should be aware of the scrollbar and only use the actual viewport width (hence its name), see: http://www.w3.org/TR/css-values/#vw-unit "Note that the initial containing block's size is affected by the presence of scrollbars on the viewport." – Niklas Brunberg May 27 '15 at 18:33
  • 1
    @NiklasBrunberg That is the behavior I was expecting, but it's not what's happening. I'll try putting together an example. – hal May 27 '15 at 18:37
  • I believe @redelschaap might have your answer to this part of your question already. Your `.parent { width: 30rem; }` or the `body`'s margin looks like it might be the culprit here. – Niklas Brunberg May 27 '15 at 18:40
  • @NiklasBrunberg Neither are causing this. Check out the jsfiddle I just added. – hal May 27 '15 at 18:48
  • I have a working solution based on your fiddle: https://jsfiddle.net/1jh1cybc/4/ I will update my answer. – Niklas Brunberg May 27 '15 at 19:03
  • 2
    @NiklasBrunberg Hmmm, but with max-width 100% you are just overriding the width set in vw. So you can just change the width to 100% instead. I want the width to actually be 100vw(the parent is not neccessarily 100% of the the viewport. I changed the jsfiddle to show the issue with a parent that is not 100% width. – hal May 27 '15 at 19:59
  • Right, but that is assuming that all it's ancestors are position static. In my example the parent was set to relative. I was asking if there is a way other than setting all ancestors to static or changing the markup. – hal May 27 '15 at 20:03
  • So i guess the answer is no, those are the only options? – hal May 27 '15 at 20:06
  • You can't remove `position: relative;` from `.parent`? Is anything preventing you from using `position: fixed;` on `.box`? – Niklas Brunberg May 27 '15 at 20:13
  • I can set all the ancestors to `position: static`, it just means a lot more work getting everything to layout correctly, and possibly having to change the markup. Which is what prompted the question. No, `.box` cannot be fixed. – hal May 27 '15 at 20:21
  • More or less. The remainder of the problem is that `vw` is calculated once but not re-calculated once the scrollbars appear. According to the w3.org link previously posted this re-calculation happens "However, when the value of ‘overflow’ on the root element is ‘auto’, any scroll bars are assumed not to exist.". This, IMO is one of the weaknesses with the `vw` unit, it should be re-calculated (but I assume there is a good reason for it working the way it does). Mayhaps this can help http://stackoverflow.com/questions/986937/how-can-i-get-the-browsers-scrollbar-sizes? – Niklas Brunberg May 27 '15 at 21:00
  • `-moz-box-sizing` and `-moz-calc` is unnecessary; Firefox dropped the prefix long ago. Scrollbar is always 17px wide/high across all desktop browsers, if one is present. – Sheepy Sep 24 '15 at 05:15
  • @Sheepy. First, scrollbars are not always 17px, desktop Safari for example are 15 points (https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/WindowScrolling.html). Second, we still experience >1.5% visitors with FF <30 which requires -moz- prefixes not to completely break layout. YMMV of course, but not everyone can drop the prefixes yet. Third, someone with UI Scaling activated in their OS might change the scrollbar width/height as well. In conclusion we would do better not to assume its width. – Niklas Brunberg Sep 28 '15 at 09:51
3

I was also struggling with this, and I also thought of CSS variables as the solution. CSS variables aren't supported in IE11 though so I tried something else:

I fixed it by calculating the width of the scroll bar: subtracting the width of the body (not including scroll bar) from the width of the window (including the scroll bar). I use this to add it to the 100% of the body, see plusScrollBar variable.

JS:

    // calculate width of scrollbar and add it as inline-style to the body
var checkScrollBars = function() {
    var b = $('body');
    var normalw = 0;
    var scrollw = 0;
    normalw = window.innerWidth;
    scrollw = normalw - b.width();

    var plusScrollBar = 'calc(' + '100% + ' + scrollw + 'px)'
    document.querySelector('body').style.minWidth = plusScrollBar;
}();

CSS:

html{
    overflow-x: hidden;
}

Why I like this: it takes in consideration that not all scrollbars are the same width or considered as conserved space at all. :)

Cyd
  • 59
  • 3
1

I had the same issue and it was fixed when I added:

html, body { overflow-y: auto; }

I added the comment:

/* this fixes a 100vw issue, removing the horizontal scrollbar when it's unneeded */

It works at least with Firefox, Chrome, Opera and Internet Explorer 11 (I used browserling for IE).

I don't know why it works and if it works in all cases, though.

EDIT: The horizontal scrollbar disappeared, but I noticed it's still horizontally scrollable using the cursor keys and touch screens...

Pedro Amaral Couto
  • 2,056
  • 1
  • 13
  • 15
0

overflow-x: clip; does the job.

Apofis
  • 34
  • 4
  • 3
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: https://stackoverflow.com/help/how-to-answer . Good luck – nima Oct 08 '21 at 11:27
-1

I had the same problem. When I changed the vw units to percentage, horizontal scrollbar disappeared.

  • 1
    That sets it to the % of its parent. It just worked for you because of the lucky situation of the parent of that moment happening to be the screen width. That is not always the case, and does nothing to solve the issue with vw. – Jimbo Jonny Oct 17 '20 at 21:24
-1

If you're working in a framework (ASP.NET for example) where there's possibly a parent element wrapping around the html, then setting the html's max-width to 100% will solve the problem without using the "band-aid" solution overflow-x: hidden.

html {
   max-width: 100%;
}
-2

An element with width: 100vw only causes horizontal scrollbars when one of it's parents has a horizontal padding. Otherwise it should fit in well.

Check this fiddle: http://jsfiddle.net/1jh1cybc/ The .parent2 has a padding, which causes the inner .box to break out of it's parent width.

Edit:

In your case I guess your body has a margin. Check this fiddle out with you code and try to remove the body css rule: http://jsfiddle.net/1jh1cybc/1/

redelschaap
  • 2,774
  • 2
  • 19
  • 32
  • My body has no margin. Check out the jsfiddle I just added to the question. It's modified form yours. – hal May 27 '15 at 18:47
  • Your second example has no vertical scrollbar, so there is no horizontal overflow. – hal May 27 '15 at 18:54
  • I've adjusted my second example, giving the box 1000px height, still no horizontal scroll bar: http://jsfiddle.net/1jh1cybc/3/ – redelschaap May 27 '15 at 19:00
  • I've looked into your fiddle, but I don't have a horizontal scroll bar, only a vertical one... https://dl.dropboxusercontent.com/u/9719519/2015-05-27/Fiddle.jpg – redelschaap May 27 '15 at 19:05
  • What browser/OS are you using? I don't recognize the scroll bar at all. – hal May 27 '15 at 19:55
  • I use Chrome 43.0.2357 on Windows 7 – redelschaap May 27 '15 at 20:11
  • Hmmm, well in your case the vw is actually the same, but because your scroll bar is overlayed over the page, unlike standard "solid" scrollbars, it does not cause the same issue. The spec does say that vw is calculated with the assumption that there are no scrollbars if overflow is set to auto. – hal May 28 '15 at 09:39
  • But browsers calculate the viewport size without scroll bar size of they are solid (not semi transparent). Or at least they should, so maybe it's a bug in specific versions? – redelschaap May 28 '15 at 09:58
  • When the browser calculates vw units "any scroll bars are assumed not to exist". So 100vw is 100% of the visible viewport plus the width of the scrollbar. The spec says it should do this "when the value of overflow on the root element is auto", but I've tried setting `html { overflow: scroll; }` and it doesn't change anything. – hal May 28 '15 at 10:40
-2

Here's what I do:

div.screenwidth{
    width:100%;  /* fallback for browsers that don't understand vw or calc */
    width: calc(100vw - 17px); /* -17 because vw is calculated without the scrollbar being considered & 17px is width of scrollbars */
    position:relative;  /* use this if the parent div isn't flush left */
    right: calc((100vw - 17px - 100% )/2);

}

  • 3
    What about devices where there is no scroll bar? The element will end up being 17px short. Also 17px is not a universal width of all scroll bars. I just checked in my current version of chrome and the scroll bar was 15px. – hal Oct 08 '15 at 19:23
  • 1
    Yes, it's 17px short, but the positioning centers it and I personally (since there is no actual solution) prefer to live with the 8px gap on either side rather than have the horizontal scroll. – innocuous_name Oct 12 '15 at 15:44
-2

I fixed this on my site by adding body{overflow-x:hidden} to the page in question.

Works on your example too.

Adam
  • 403
  • 3
  • 8
  • I addressed this in my answer. It is not a solution, but it may be appropriate in some situations where you don't mind losing part of your element, because the 100vw element will still overflow. read #4 of my answer – hal Mar 20 '16 at 16:16
-2

Here's my solution to the problem of 100vw adding a horizontal scroll:

html {
width: calc(100% + calc(100vw - 100%));
overflow-x: hidden;
}

.box {
width: calc(100% + calc(100vw - 100%));
}
moy2010
  • 854
  • 12
  • 18
  • 3
    There's a lot of useless clutter in your answer. Here is a simpler version that works just as well: **html{overflow-x:hidden;}** – Eliezer Berlin Apr 16 '19 at 08:21
  • Your reply does not contribute to OP's question. I recommend you to read it again. – moy2010 Apr 16 '19 at 13:44
  • I recommend you review your answer. There's literally no functional difference between what you wrote in your answer, and html{width:100%;overflow-x:hidden;} .box{width:100vw;}, except a lot of useless math that does nothing. – Eliezer Berlin Apr 17 '19 at 14:05
  • {width: 100%} and {width: 100vw} are not the same. Maybe that's where your misunderstanding comes from. – moy2010 Apr 17 '19 at 15:03
  • 100% and 100vw are the same when they're put on the element. I'm afraid I'm not misunderstanding anything, your answer is just absurdly overcomplicated. Sorry, @moy2010. – Eliezer Berlin Apr 18 '19 at 07:31
  • 6
    100% + (100vw - 100%) **=** 100% + 100vw - 100% **=** 100vw. The math does nothing aside from concealing the fact that this answer works because you have overflow-x on the body. – Eliezer Berlin Apr 18 '19 at 07:35