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;
}
}
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.
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].
hasScrollBar = function() { return this. get(0). scrollHeight > this. height(); } })(jQuery);
Do you want to know if an element can ever scroll or can currently 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'
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
.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With