Many assume that width: 100vw is the same as width: 100% . This is true on a page that doesn't scroll vertically, but what you might not realize is that the viewport actually includes the scrollbars, too.
It seems the outcome was for vw to include the scrollbar width as authors could specify overflow-y:scroll on html if they were expecting 100% widths to be involved and then the horizontal scrollbar would not be triggered when 100vw was in play.
To get the width of the scrollbar, you use the offsetWidth and clientWidth of the Element : The offsetWidth returns the width of the Element in pixels including the scrollbar. The clientWidth returns the with of the Element in pixels without the scrollbar.
The area that is visible is called the viewport. The size of the viewport can be defined using the width and height attributes of the <svg> element. In this example, the viewport has an aspect ratio of 3:4 and is, by default, 400 by 300 units, with a unit generally being a CSS pixel.
One way to do this is with calc. As far as i know, 100% is the width including scrollbars. So if you do:
body {
width: calc(100vw - (100vw - 100%));
}
You get the 100vw minus the width of the scrollbar.
You can do this with height as well, if you want a square that's 50% of the viewport for example (minus 50% of the scollbar width)
.box {
width: calc(50vw - ((100vw - 100%)/2))
height: 0
padding-bottom: calc(50vw - ((100vw - 100%)/2))
}
I do this by adding a line of javascript to define a CSS variable once the document has loaded:
document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");
then in the CSS you can use var(--scrollbar-width)
to make any adjustments you need for different browsers with/without scrollbars of different widths. You can do something similar for the horizontal scrollbar, if needed, replacing the innerWidth
with innerHeight
and clientWidth
with clientHeight
.
According to the specs, the viewport relative length units do not take scrollbars into account (and in fact, assume that they don't exist).
So whatever your intended behavior is, you cannot take scrollbars into account when using these units.
COPY & PASTE solution
Here is an easy drop-in solution based on user11990065's answer to set a css variable --scrollbar-width and keep it updated on resizes. It also gets calculated on DOMContentLoaded and load events so that you don't have to worry about size changes during the initial rendering phase.
You can just copy and paste it to your code as it is vanilla JS (or wrap it in a 'script' tag and paste it directly into your HTML code:
function _calculateScrollbarWidth() {
document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");
}
// recalculate on resize
window.addEventListener('resize', _calculateScrollbarWidth, false);
// recalculate on dom load
document.addEventListener('DOMContentLoaded', _calculateScrollbarWidth, false);
// recalculate on load (assets loaded as well)
window.addEventListener('load', _calculateScrollbarWidth);
If you have dynamic height changes in your page that might show / hide the scrollbar, you might want to look into Detect Document Height Change with which you can trigger the recalculation also on height changes.
As the value is calculated with JS and set to a fixed value you can use it in calc operations in your CSS, like so:
.full-width {
width: calc(100vw - var(--scrollbar-width));
}
This will give .full-width exactly the available width.
Webkit browsers exclude the scrollbars, other include them in the returned width. This may of course lead to problems: for instance if you have dynamically generated content with ajax that add height dynamically, Safari might switch from a layout to another during page visualization... Ok, it doesn't happen often, but it's something to be aware about. On mobile, less problems, cause scrollbars are generally not showed.
That's said, if your problem is calculate exactly the viewport width without scrollbars in all browser, as far as i know, a good method is this:
width = $('body').innerWidth();
having previously set:
body {
margin:0;
}
body { overflow: overlay; }
If you don't want to overcomplicate things, this might be sufficient in certain situations. At least it fixed my issues well enough, since there was enough whitespace between the content and the viewport edges (Windows scrollbar would overlap your 20-ish most right pixels).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With