As mentioned in the title, is it possible to calculate the vw without the scrollbars in css only?

For example, my screen has a width of 1920px. vw returns 1920px, great. But my actual body width is only something like 1903px.

Is there a way for me to retrieve the 1903px value with css only (not only for direct children of the body), or do I absolutely need JavaScript for this?

Solution 1

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))
}  

Solution 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.

Solution 3

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.

Solution 4

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.

Solution 5

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;
}

Solution 6

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).

Solution 7

100vw = width of the screen with scrollbar 100% = width of the screen without scrollbar

It is always preferable to use calc(100% - 50px) while measuring the screen width. Even on windows browsers where scrollbar is visible directly, return the screen width differently when compare with macOS browsers.

Solution 8

It's possible just very "ugly" looking.

First you need to have this script running to get the scrollbar width into a css variable:

document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");

Now for example if you want "real" 80vw do this:

calc(0.8 * (100vw - var(--scrollbar-width)));

"real" 40vw

calc(0.4 * (100vw - var(--scrollbar-width)));

Solution 9

The only way I found it to work without messing your code with "calc" is to make the container element size to 100vw; Adding a wrapper around the container for overflow-x; This will make the container to be fullwidth like if the scrollbar was over the content.

<!DOCTYPE html>
<html>
<head>
	<style type="text/css">
	html{ overflow-y: scroll; }
	html, body{ padding:0; margin: 0;}
	#wrapper{ overflow-x: hidden; }
	.row{ width: 100vw; }
	.row:after{ clear: both; content: ''; display: block; overflow: hidden; }
	.row-left{ background: blue; float: left; height: 40vh; width: 50vw; }
	.row-right{ background: red; float: right; height: 40vh; width: 50vw; }
	</style>
</head>
<body>

<div id="wrapper">
<div class="row">
	<div class="row-left"></div>
	<div class="row-right"></div>
</div>
</div>


</body>
</html>

Solution 10

If the case were something similar to a slider: As posted in many answers, width 100% doesn't take into account the scrollbar, while 100vw does. In the case of having many elements that need to take the width of the window and that are nested inside a container already with 100% window width (or whose natural block width would be such), you can use:

  • Display flex for container
  • Flex: 0 0 100% for child elements

Solution 11

The vw unit doesn't take the overflow-y scrollbar into account when overflow-y is set to auto.

Change it to overflow-y: scroll; and the vw unit will be the viewport with the scrollbar. Then you can subtract the scrollbar size from the vw value using calc(). You can also define the scrollbar width, so it will be browser-independent.

Only downside to take into account. If the content fits into the screen, the scrollbar is shown anyway. Possible solution is to change from auto to scroll in javascript.

Solution 12

No, there's no way to calculate the vw without the scrollbars in CSS.

However, there's a way to solve the 100vw ruined by the scrollbar on Windows issue. You have to create a full-width element, in this case row--full-width, that beelds out of a Flex container. This solution works on both Mac and Windows:

HTML:

<section> 
  <div class="container">
    <div class="row--full-width"></div>
    <div class="row">
      <div class="card">
      </div>
      <div class="card">
      </div>
    </div>
  </div>
</section>

As you can see in the example above, the row--full-width element bleeds out of the container, and it aligns with the header even when there's a scrollbar.

Tested on Edge 18 (Win), Edge 88 (Win/Mac), and Chrome 88 (Win/Mac).

Solution 13

The easiest way is set the html & body to 100vw:

html, body{ width:100vw; overflow-x: hidden; overflow-y: auto; margin: 0; }

The only problem is the right-most will be cut a little if scrollbar is shown.

Solution 14

It's not my solution, but helps me create dropdown fullwidth menu with absolute in relative element in not fullwith span.

We should get scroll with in css var in :root and then use it.

:root{
 --scrollbar-width: calc(100vw - 100%);
}


div { margin-right: var(--scrollbar-width); }

https://codepen.io/superkoders/pen/NwWyee