CSS: How to get browser scrollbar width? (for :hover {overflow: auto} nice margins)
Solution 1
Scrollbar widths can vary between browsers and operating systems, and unfortunately CSS does not provide a way to detect those widths: we need to use JavaScript.
Other people have solved this problem by measuring the width of the scrollbar on an element:
- http://davidwalsh.name/detect-scrollbar-width (original post)
- http://jsfiddle.net/a1m6an3u/ (live example)
We create a div .scrollbar-measure
, add a scrollbar, and return its size.
// Create the div
var scrollDiv = document.createElement("div");
scrollDiv.className = "scrollbar-measure";
document.body.appendChild(scrollDiv);
// Get the scrollbar width
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
console.warn(scrollbarWidth);
// Delete the div
document.body.removeChild(scrollDiv);
This is fairly straightforward, but it is (obviously) not pure CSS.
Solution 2
innerWidth returns the width of the browser viewport including the scrollbar.
The read-only Window property innerWidth returns the interior width of the window in pixels. This includes the width of the vertical scroll bar, if one is present.
clientWidth returns the width of viewport, excluding the scrollbar, when used on a root element like document.
When clientWidth is used on the root element (the element), (or on if the document is in quirks mode), the viewport's width (excluding any scrollbar) is returned.
let scrollbarWidth = (window.innerWidth - document.body.clientWidth) + 'px';
Subtract the two values and voila, you get the width of the scrollbar.
This example would usually return: 15px
Solution 3
Whilst tomtomtom's answer works fantastically, it requires an external CSS snippet that seems somewhat unnecessary, and Yurii's answer requires jQuery, which again seems sorta overkill for this. A slightly more up-to-date and self-contained answer (which can also be used in a JavaScript module setup) is:
class TempScrollBox {
constructor() {
this.scrollBarWidth = 0;
this.measureScrollbarWidth();
}
measureScrollbarWidth() {
// Add temporary box to wrapper
let scrollbox = document.createElement('div');
// Make box scrollable
scrollbox.style.overflow = 'scroll';
// Append box to document
document.body.appendChild(scrollbox);
// Measure inner width of box
this.scrollBarWidth = scrollbox.offsetWidth - scrollbox.clientWidth;
// Remove box
document.body.removeChild(scrollbox);
}
get width() {
return this.scrollBarWidth;
}
}
This class can then be used like so:
let scrollbox = new TempScrollBox();
console.log(scrollbox.width) //-> 17 (Windows, Chrome)
Fiddle: https://jsfiddle.net/nu9oy1bc/
Solution 4
Pure CSS solution here.
I would create an invisible div on the right of your text div with forced scrollbar inside it:
<div class="container">
<div class="text">Your very and very long text here</div>
<div class="scrollbar-width"></div>
</div>
<style>
.container {
display: flex;
width: 100px; /* to make scrollbar necessary */
height: 30px; /* to make scrollbar necessary */
border: 1px solid blue;
}
.text {
flex: 1;
float: left;
overflow-y: hidden;
}
.scrollbar-width {
float: left;
overflow-x: hidden;
overflow-y: scroll;
visibility: hidden;
}
.scrollbar-width::-webkit-scrollbar { /* for debugging */
background-color: red;
}
.text:hover {
overflow-y: scroll;
}
.text:hover + .scrollbar-width {
display: none;
}
</style>
When the scrollbar-width
div is marked with visibility: hidden
it will still take space.
Now when you finally decide to show your own scrollbar, add display: none
to the invisible scrollbar-width
div, so it does not take space anymore, and therefore your text
div fills all available space and becomes wider.
In case you wish to verify, remove visibility: hidden;
from the style of scrollbar-width
div. Then you can see how this placeholder div is initially taking space (it appears red according to the "for debugging" style above) and on hover it disappears and is replaced by the text div-s scrollbar.
JS fiddle link: https://jsfiddle.net/rb1o0tkn/
Solution 5
My solution, works perfectly in latest Chrome, Mozilla, Edge, and Opera. Don't know about Safari
const scrollbarWidth = window.innerWidth - document.documentElement.offsetWidth
Related videos on Youtube
Jacek Kowalewski
Hello! I'm an All-end developer, and founder of Flowtar Studio.
Updated on April 04, 2022Comments
-
Jacek Kowalewski about 2 years
I'm not sure if title is clear, so few words of explanation. I've got few little elements, let say
div
's (200px
x400px
CONSTANT). Inside each of them, there is a paragraph with about 20 lines of text. Of course, this it to much for a poor little div. What I want to do is:- Normally
div
hasoverflow: hidden
property. - On mouse over (
:hover
) this property is changed tooverflow: auto;
, and the scrollbar appears.
What is the problem? I want a little space (padding or margin) between the paragraph text and the scrollbar. Let's say that paragraph has a symetrical
margin: 20px;
. But after:hover
triggers on, the scrollbar appears, and the whole right side of the paragraph is moved"scrollbar-width" px
to the left. Sentences are broken in other lines, and the whole paragraph look different, which is quite annoying and not user friendly. How can I set my CSS, so the only change after hover will be the appearance of scroolbar?In other words:
/* Normally no scrollbar */ div {display: block; width: 400px; height: 200px; overflow: hidden;} /* On hover scrollbar appears */ div:hover {overflow: auto;} /* Paragraph margin predicted for future scrollbar on div */ div p {margin: 20px (20px + scrollbarwidth px) 20px 50px;} /* With scrollbar margin is symmetrical */ div:hover p {margin: 20px;} /* With scrollbar */
I have done a little snippet for it, with exaplanation of my problem in
strong
. I hope everything is clear :). I'm searching for a solution for almost two hours now, so I think my question is quite unique and interesting.div.demo1 { width: 400px; height: 200px; overflow: hidden; background: #ddd; } div.demo1:hover { overflow: auto; } div.demo1 p { background: #eee; margin: 20px; } div.demo2 { width: 400px; height: 200px; overflow: hidden; background: #ddd; } div.demo2:hover { overflow: auto; } div.demo2 p { background: #eee; margin: 20px 40px 20px 20px; } div.demo2:hover p { margin: 20px 20px 20px 20px; }
<div class="demo1"> <p> This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. </p> </div> <br> <br> <strong>As you can see, on hover right side of the paragraph "moves" left. But I want something like:</strong> <br> <br> <div class="demo2"> <p> This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. </p> </div> <strong>But with a "perfect" scrollbar width (in this example I used 20px)</strong>
-
Jacek Kowalewski over 9 yearsBTW. I know that I can hardcode the paragraph width and use for example
350px
, but this is not the solution that I want - it is ugly and assumes that the max width of scrollbar is only 50px :P. Thx for your time, best regards. -
SW4 over 9 yearsJS is the only option- as such possible duplicate: stackoverflow.com/questions/13382516/…
- Normally
-
Jacek Kowalewski over 9 yearsYour answer is really great, +1 from me. However if you don't mind I will wait few hours with accept, maybe someone will come with a pure CSS solution. If not, I will of course accept it. Thanks for your time, and great reasearch.
-
mtx almost 7 yearsi tried this code on Linux and Chrome and console warning shows 0 (zero)... any idea what could be wrong?
-
Koh over 6 years@mtx Did you define the CSS for your scrollbar-measure class? It probably hadn't overflown. The jsfiddle should help.
-
TacB0sS over 5 yearsvery nice clean code... works on mac.. wonder if this works on mobile browsers as well!!
-
Ferrybig over 5 yearsThis fails to account for the systems where the scrollbar width is dynamic, like on ma OS there is an option to only show the scrollbar when hovering. As there is no hover on your DIV, it thinks the scrollbar width is 0
-
matt freake over 4 yearsAdding an explanation would improve this answer
-
Raphael Parent over 2 yearsSimplest and easiest method, using this to set a CSS variable on the body (or the appropriate element) and using it in the stylesheet has proven to be reliable on my end.
-
Chris over 2 yearsI believe this only works if the document.body has 100% width. It will not work if your document.body has a max-width. Dennis Icllchishin's works if your document.body has a max-width:
const scrollbarWidth = window.innerWidth - document.documentElement.offsetWidth
-
krulik over 2 yearsmost accurate solution
-
Kostiantyn Ko about 2 yearsthis returns
auto
in my Chrome -
CanonicEpicure about 2 yearshm.. yup, same here. something must have changed since this answer (chrome version obsviously)