Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to accurately determine if an element is scrollable?

Tags:

I'm working on a custom knockout binding that determines if a particular element is being scrolled, and updates the bound observable with the element's top relative to the viewport. Right now, the binding seems to work, but I have some worries about whether there are some circumstances where it won't.

HTML:

Scroll position: <span data-bind="text: scrollPosition"></span>

<div class="longdiv">    
    <p data-bind="scroll: scrollPosition">This is some text.</p>
    <div class="shim"></div>
</div>

CSS:

.longdiv {
    width: 200px;
    height: 200px;
    overflow: scroll;
    border: 1px solid black;
}

JS:

ko.bindingHandlers.scroll = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var firstScrollableContainer = null;

        var binding = allBindings.get('scroll');

        $(element).parents().each(function (i, parent) {
            if ($(parent).css('overflow')=='scroll') {
                firstScrollableContainer = parent;
                return false;
            }
        });

        firstScrollableContainer = firstScrollableContainer || window;                

        binding(element.getBoundingClientRect().top);

        $(firstScrollableContainer).scroll(function() {            
            binding(element.getBoundingClientRect().top);
        });
    }
};

var ViewModel = function() {
    var self = this;

    self.scrollPosition = ko.observable(0);
};

ko.applyBindings(new ViewModel());

JSFiddle

The binding takes the element and uses jQuery to walk up the parent chain looking to see if the parent element has overflow: scroll set. If it finds a div with overflow: scroll, it binds an event handler to that element's scroll event. If it doesn't find a parent with overflow: scroll, it then binds to the scroll event of the window.

So what I'm looking for, given a document structured like so:

body > div > div > div > p

is the containing element closest to p that can be scrolled, so that I can attach an event handler to it.

My question is: is looking at overflow: scroll a sufficient test to see if a parent element can be scrolled? If not, what should I be looking at?

EDIT: Based on your helpful comments and answers, here is the solution I came up with:

function scrollable(element) {
    var vertically_scrollable, horizontally_scrollable;

    var e = $(element);

     if (   e.css('overflow') == 'scroll' 
         || e.css('overflow') == 'auto'
         || e.css('overflowY') == 'scroll'
         || e.css('overflowY') == 'auto'
         || e.css('height') != 'none'
         || e.css('max-height') != 'none'                          
         ) {
         return true;
    } else {
        return false;
    }
}
like image 297
McCroskey Avatar asked Apr 29 '15 23:04

McCroskey


People also ask

How do you know if an element is scrollable?

Use the scrollTop and scrollLeft properties. If these are greater than 0, scrollbars are present. If these are 0, then first set them to 1, and test again to know if getting a result of 1.

How do I check if a div is scrolled?

To check if a div is scrolled all the way to the bottom with jQuery, we can call the on method with 'scroll' and a scroll event handler. then we can write: const chkScroll = (e) => { const elem = $(e. currentTarget); if (elem[0].

How do you check if there is a scrollbar JavaScript?

hasScrollBar = function() { return this. get(0). scrollHeight > this. height(); } })(jQuery);


3 Answers

Do you want to know if an element can ever scroll or can currently scroll?

Can an element ever scroll?

An element can scroll if it has a fixed height (or max-height) and overflow-y is scroll or auto. But since it's not easy to tell if an element's height is fixed or not, it's probably sufficient to just check overflow-y:

e.css('overflow-y') == 'scroll' || e.css('overflow-y') == 'auto'

Can an element scroll right now?

An element can scroll right now if its scrollHeight is greater than its clientHeight and if it has a scrollbar, which can be determined by comparing clientWidth and offsetWidth (taking margins and borders into account) or checking if overflow-y is scroll or auto.

like image 178
Michael Best Avatar answered Nov 01 '22 04:11

Michael Best


This is probably the safest solution (jQuery required, for plain JavaScript see below):

$.fn.isHScrollable = function () {
    return this[0].scrollWidth > this[0].clientWidth;
};

$.fn.isVScrollable = function () {
    return this[0].scrollHeight > this[0].clientHeight;
};

$.fn.isScrollable = function () {
    return this[0].scrollWidth > this[0].clientWidth || this[0].scrollHeight > this[0].clientHeight;
};

Then you can check if an element is scrollable like this:

$(parent).isScrollable();

For usage without jQuery you can implement functions like this:

function isScrollable(element) {
    return element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight;
};

var myParent = document.getElementById('myParent')
isScrollable(myParent)

like image 40
RafaelKr Avatar answered Nov 01 '22 04:11

RafaelKr


Merging the two answers together, and adding a little something of my own, this is what I use to check Vertical scrolling. It can be easily converted for other cases. (H & VH)

function isScrollable(e){
    if( e.scrollTopMax !== undefined )
        return e.scrollTopMax > 0; //All Hail Firefox and it's superior technology!

    if( e == document.scrollingElement ) //If what you're checking is BODY (or HTML depending on your css styles)
        return e.scrollHeight > e.clientHeight; //This is a special case.

    return e.scrollHeight > e.clientHeight && ["scroll", "auto"].indexOf(getComputedStyle(e).overflowY) >= 0



}

I tested this on Firefox, and Chromium. Both Linux. you might still wanna check them for yourself though.

like image 35
aliqandil Avatar answered Nov 01 '22 03:11

aliqandil