149

What's the fastest way of checking whether an element has scroll bars?

One thing of course is checking whether element is larger than its viewport, which can easily be done by checking these two values:

el.scrollHeight > el.offsetHeight || el.scrollWidth > el.offsetWidth

but that doesn't mean that it has scrollbars as well (so it can actually be scrolled by humans).

Question

How do I check for scrollbars in a 1 cross browser and 2 javascript only (as in no jQuery) way?

Javascript only, because I need as small overhead as possible, because I'd like to write a very fast jQuery selector filter

// check for specific scrollbars
$(":scrollable(x/y/both)")

// check for ANY scrollbar
$(":scrollable")

I suppose I'd have to check for overflow style settings but how do I do that in a cross browser way?

Additional edit

Not only overflow style settings. Checking whether an element has a scrollbar isn't as trivial as it seems. The first formula I've written above works fine when element doesn't have a border, but when it does (especially when border is of considerable width), offset dimension can be larger than scroll dimension but the element can still be scrollable. We actually have to subtract borders from offset dimension to get the actual scrollable viewport of the element and compare that to scroll dimension.

For future reference

:scrollable jQuery selector filter is included in my .scrollintoview() jQuery plugin. Complete code can be found in my blog post if anybody needs it. Even though it didn't provide the actual solution Soumya's code considerably helped me solve the problem. It pointed me in the right direction.

Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404

12 Answers12

180

I found this somewhere a couple of weeks ago. It worked for me.

var div = document.getElementById('container_div_id');

var hasHorizontalScrollbar = div.scrollWidth > div.clientWidth;
var hasVerticalScrollbar = div.scrollHeight > div.clientHeight;

/* you'll get true/false */
Limon Monte
  • 52,539
  • 45
  • 182
  • 213
jzhinga
  • 1,912
  • 1
  • 11
  • 3
  • 18
    You've obviously had a simplified example. What if your container has `overflow:hidden` set on it? There'd be excess content but it's still not scrollable. The problem is by far not as simple as it may seem. – Robert Koritnik Feb 18 '11 at 12:56
  • 13
    This might not be the accepted solution, but it worked for me. I'm not sure why you would scroll it if it's hidden. – vol7ron Aug 17 '11 at 19:38
  • 1
    This might not be the accepted solution, but it worked for me. Robert, for those cases, couldn't you also fetch the css overflow property to test for those cases (eg `div.scrollHeight>div.clientHeight && !(div.style.overflow && div.style.overflow == 'hidden')`)? – vol7ron Sep 15 '11 at 15:00
  • 7
    This fails in many cases. If your element has overflow: visible; width: 200px; and has a child with a 500px width, your element has no scroll bars but has a scrollWidth of 500px and a clientWidth of 200px. – Joseph Lennox Mar 05 '14 at 21:17
  • 1
    If overflow is visible or hidden, there usually are no scrollbars. – Hubert Grzeskowiak Jul 21 '16 at 18:27
  • thanks - helped me find my answer for IE11 which needed a slight tweak to this logic +1 - see my IE answer below – danday74 Dec 21 '17 at 16:36
  • Downvoted because it's not 100% correct. If it work's it doesn't seem it's the best and most complete answer. In my opinion user113716's answer is the best and works for all cases (e.g. there are browsers where scrollbar does not affect width at all) – besciualex Oct 02 '18 at 13:08
  • It fails if the element has borders. Client height will be scroll height - 2 – Vinícius Negrão Mar 19 '21 at 15:08
  • This implementation in the NPM package `scrollparent` based on checking overflows seems to bit more robust if you need it: https://github.com/olahol/scrollparent.js/blob/master/scrollparent.js – Matt Jensen Feb 28 '23 at 16:15
20

This may seem (or be) a little hackish, but you could test the scrollTop and scrollLeft properties.

If they're greater than 0, you know there are scrollbars. If they're 0, then set them to 1, and test them again to see if you get a result of 1. Then set them back to 0.

Example: http://jsfiddle.net/MxpR6/1/

function hasScroll(el, direction) {
    direction = (direction === 'vertical') ? 'scrollTop' : 'scrollLeft';
    var result = !! el[direction];

    if (!result) {
        el[direction] = 1;
        result = !!el[direction];
        el[direction] = 0;
    }
    return result;
}

alert('vertical? ' + hasScroll(document.body, 'vertical'));
alert('horizontal? ' + hasScroll(document.body, 'horizontal'));

I believe there's a different property for IE, so I'll update in a minute with that.

EDIT: Appears as though IE may support this property. (I can't test IE right now.)

http://msdn.microsoft.com/en-us/library/ms534618(VS.85).aspx

user113716
  • 318,772
  • 63
  • 451
  • 440
  • It's much easier to check for scrollbars if you compare `offsetHeight` and `clientHeight`. The latter is always smaller by the scrollbar size when scrollbar is present. – Robert Koritnik Feb 04 '11 at 11:53
  • @Robert: Not sure about easier, but I do see how it works. Then I assume if an element has `overflow:scroll`, you want that to be reported as scrollable whether or not the scrollbars are enabled. Of course my solution could have issues if there's an onscroll event attached. – user113716 Feb 04 '11 at 14:44
  • Not really. If you check my [jQuery plugin](http://erraticdev.blogspot.com/2011/02/jquery-scroll-into-view-plugin-with.html) I have to find actual scrollable ancestor otherwise I can't scroll an element into view. – Robert Koritnik Jul 22 '11 at 12:04
  • 8
    @RobertKoritnik Not true; some browsers may have scrollbars that do not reduce the width. On an OS X for example. Or touch devices. – daniel.gindi Feb 10 '14 at 08:34
  • 1
    always return false – Fatih Erol Nov 01 '18 at 22:54
  • as @FatihErol it is always false now, tested on FF63 x64 – Joe DF Nov 08 '18 at 15:16
  • Check for if element has overflow hidden needs to be added else this logic works +1 – sudazzle Nov 26 '21 at 21:28
19

Try:

For vertical scroll bar

el.scrollHeight > el.clientHeight

For horizontal scrollbar

el.scrollWidth > el.clientWidth

I know this works for IE8 and Firefox 3.6+ at least.

Gary
  • 1,001
  • 8
  • 17
  • 2
    I pointed this out yes that this tells me taht a certain element is larger than it seems, but that doesn't mean it displays scrollbars. It can as well have `overflow:hidden` and it wouldn't be scrollable anymore. – Robert Koritnik Feb 02 '11 at 22:10
  • 1
    And checking with clientHeight/clientWidth values doesn't give good results, because elements can have borders as well, which are not included in this measure. Check my formula. It works better than yours. – Robert Koritnik Feb 02 '11 at 22:12
  • That's true about using offsetHeight/offsetWidth for checking scrollbars instead of clientHeight/clientWidth. Thanks for pointing that out. – Gary Feb 04 '11 at 15:43
  • @RobertKoritnik - so just check if that specific element has `overflow:hidden` on it...this is still the correct answer in my opinion, since it's the simplest. – vsync Mar 30 '14 at 14:14
15

Here is yet another solution:

As a few people pointed out, simply comparing offsetHeight and scrollHeight is not enough since they differ on elements with overflow hidden, etc., that still don't have scrollbars. So here I'm also checking if overflow is scroll or auto on the computed styles for the element:

var isScrollable = function(node) {
  var overflowY = window.getComputedStyle(node)['overflow-y'];
  var overflowX = window.getComputedStyle(node)['overflow-x'];
  return {
    vertical: (overflowY === 'scroll' || overflowY === 'auto') && node.scrollHeight > node.clientHeight,
    horizontal: (overflowX === 'scroll' || overflowX === 'auto') && node.scrollWidth > node.clientWidth,
  };
}
lotif
  • 3,401
  • 2
  • 19
  • 18
  • If `overflow*` is `scroll` there will always be a scrollbar, so I think you want `vertical: overflowY === 'scroll' || overflowY === 'auto' && node.scrollHeight > node.clientHeight`, etc. (i.e. remove the brackets so `scroll*` vs `client*` is only tested when `overflow*` is `auto`). Otherwise this appears to be the most correct solution. – Jake Jan 06 '18 at 23:17
  • @Jake If scrollHeight < clientHeight on scroll overflow, the scrollbars are going to be there but they are going to be disabled (i.e. the element is not scrollable). So I think it depends on the application. Your modification is valid of you merely want to check for scrollbars, doesn't matter the state. For me, when I came up with this, I was looking to implement autoscroll for a component, so having disabled scrollbars is useless in that case. That also appears to be the case for this question (it needs to "be scrolled by humans") – lotif Jan 08 '18 at 01:39
6

I maybe a little late to the party, but...

I believe you can detect for scrollbars with e.offsetWidth vs. e.clientWidth. Offset width includes borders and scrollbars, padding and width. Client width includes padding and width. Please see:

https://developer.mozilla.org/en/DOM/element.offsetWidth (second image) https://developer.mozilla.org/en/DOM/element.clientWidth (second image)

You need to check:

  1. Whether or not the element has overflow set to auto/scroll (including overflowX/Y) using the computed/cascaded/current style.
  2. If the element does have overflow set to auto/scroll. Establish the offsetWidth and clientWidth.
  3. If the clientWidth is less than the offsetWidth - right border (found again through the computed/cascaded/current style), then you know you have a scrollbar.

Do the same for the vertical (offset/clientHeight).

IE7 reports a clientHeight of 0 for some elements (I haven't checked why), therefore you always need the first overflow check.

Hope this helps!

5

There are several problems in case of checking the existence of scrollbars one of which is that in mac you don't have any visible scrollbar so both all the solutions above wouldn't give you an accurate answer.

So because the browser's rendering isn't very frequent you can check the having scroll with changing scroll and then setting it back:

const hasScrollBar = (element) => {
  const {scrollTop} = element;

  if(scrollTop > 0) {
    return true;
  }

  element.scrollTop += 10;

  if(scrollTop === element.scrollTop) {
    return false;
  }

  // undoing the change
  element.scrollTop = scrollTop;
  return true;
};
hakobpogh
  • 632
  • 6
  • 13
  • 1
    +10 is not necessary lower value should work as well. – sudazzle Nov 26 '21 at 21:29
  • @sudazzle the issue is that some browsers work differently related to the zoom. +10 is just a margin, but if you're not considering the zoom, then it'll work even with +1 or +2. – hakobpogh Dec 07 '21 at 14:15
2

This function takes an element and a direction (either vertical, horizontal, or both) as arguments and returns true if the element has scroll bars in the specified direction(s), and false otherwise.

function hasScrollbar(el, direction = 'both') {
  const style = window.getComputedStyle(el);
  if (direction === 'vertical' || direction === 'both') {
    if (el.clientHeight < el.scrollHeight || style.overflowY === 'scroll' || style.overflowY === 'auto') {
      return true;
    }
  }

  if (direction === 'horizontal' || direction === 'both') {
    if (el.clientWidth < el.scrollWidth || style.overflowX === 'scroll' || style.overflowX === 'auto') {
      return true;
    }
  }

  return false;
}

enter image description here

Test on this page:

hasScrollbar(document.querySelector('.lang-js.s-code-block'), 'vertical')
Volod
  • 1,283
  • 2
  • 15
  • 34
1

For IE11 (Internet Explorer 11) I had to change the logic to:

// Subtract 3 (a small arbitrary number) to allow for IE reporting a difference of 1 when no scrollbar is present
var hasVerticalScrollbar = div.scrollHeight - 3 > div.clientHeight;

This is because IE reports scrollHeight as 1 larger than clientHeight when no scrollbar is present but approx 9 larger when a scrollbar is present

danday74
  • 52,471
  • 49
  • 232
  • 283
0

If you need to know if theres a scrollbar present for the whole webpage and with full browser support you can use this:

const hasScrollbar = document.body.scrollHeight > window.innerHeight

It's important to use window.innerHeight instead of document.body.clientHeight because in some mobile browsers clientHeight will not get the size of the address bar but scrollHeight will, so you get wrong calculations.

Sebastian Hernandez
  • 2,257
  • 4
  • 23
  • 32
0

Most likely for the horizontal scrollbar, it will be a height based calculation something like this

element.offsetHeight - element.clientHeight?

In a nutshell, for horizontal scrollbars, check the scrollbars based on height calculation, and for vertical scrollbars it's vice versa.

Vaibhav More
  • 212
  • 1
  • 14
Jenifer
  • 47
  • 5
-1

Just messing around here as none of the above solutions worked out for me (so far). I have found some success with comparing a Div's scrollheight against its offsetHeight

var oh = $('#wrapDiv').get(0).offsetHeight;
var sh = $('#wrapDiv').get(0).scrollHeight;

It seems to give me an acurate comparison...so far. Does someone know if this is legitimate?

Michael
  • 173
  • 1
  • 3
  • 2
    I think you want scrollHeight and clientHeight: http://stackoverflow.com/questions/4106538/difference-between-offsetheight-and-clientheight – rich remer Dec 11 '15 at 00:09
-8

none of this answers are correct. you have to use this :

var div = document.getElementById('container_div_id');

var hasHorizontalScrollbar = (div.offsetWidth > div.clientWidth);
var hasVerticalScrollbar = (div.offsetHeight > div.clientHeight);
hamid
  • 852
  • 11
  • 27
  • @moto what accepted answer? and why would you think that a parentheses is included there to turn something into boolean, although it doesn't work and I think the correct answer would be to check checking `el.scrollX` to `el.clientX` where x belong to {Width, Height} – Hossein Alipour Mar 19 '22 at 16:49