Robert Koritnik
Robert Koritnik

Reputation: 105091

Check whether HTML element has scrollbars

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.

Upvotes: 161

Views: 155359

Answers (13)

vsync
vsync

Reputation: 130810

CSS-only solution:

animation-timeline allows to infer a scrollable element and style it accordingly:

article {
 /* base demo styles: */
 height: 100px;
 width: 300px;
 border: 4px solid red;
 overflow: auto;
 resize: vertical;

 /* the trick (and @keyframes below): */
 animation: scroll-shadows;
 animation-timeline: scroll(self);
 animation-range: 0 100%;
}

/* simulate content */
article::before{
  content: 'Resize me';
  display: block;
  height: 150px;
}

@keyframes scroll-shadows {
 from, to { border-color: green; }
}
<article></article>

👉 See support table (spoilers - no Firefox)

while this answer might not be helpful to most now, it will certainly become more and more relevant as time passes and support grows.

Also, for us who are lucky enough to work on Chrome-only products, this can be used right now.

Upvotes: 1

Jenifer
Jenifer

Reputation: 47

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.

Upvotes: 0

Volod
Volod

Reputation: 1437

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

Upvotes: 2

Sebastian Hernandez
Sebastian Hernandez

Reputation: 2376

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.

Upvotes: 3

hakobpogh
hakobpogh

Reputation: 677

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

Upvotes: 5

danday74
danday74

Reputation: 57271

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

Upvotes: 1

hamid
hamid

Reputation: 860

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

Upvotes: -8

jzhinga
jzhinga

Reputation: 2200

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 */

Upvotes: 208

lotif
lotif

Reputation: 3531

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

Upvotes: 16

Michael
Michael

Reputation: 173

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?

Upvotes: -1

user140628
user140628

Reputation:

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!

Upvotes: 6

user113716
user113716

Reputation: 322622

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

Upvotes: 19

Gary
Gary

Reputation: 1007

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.

Upvotes: 19

Related Questions