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.
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 */
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.
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
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,
};
}
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:
Whether or not the element has overflow set to auto/scroll (including overflowX/Y) using the computed/cascaded/current style.
If the element does have overflow set to auto/scroll. Establish the offsetWidth and clientWidth.
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!
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;
};
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
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.
Is isn't for horizontal scrollbar it will be of height based calculation something like
element.offsetHeight - element.clientHeight
?
To conclude, for horizontal scrollbars, do height based calculation and for vertical scrollbars vice versa.
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?
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);
Related
I've found that classic MDN formula to check if content has been scrolled to the bottom
element.scrollHeight - element.scrollTop === element.clientHeight
doesn't work for all cases any more. For example if you change the scale of content to something bigger than 100% for the demo page on MDN you will not get the right result. This happens because now browsers use subpixel precision on elements. Particularly scrollTop for not 100% scale is fractional value now. Actually the problem happens because of === sign in the formula above.
So the simplest but still reliable solution is wanted.
My current solution:
function isContentScrolledToBottom(element) {
const rest = element.scrollHeight - element.scrollTop;
return Math.abs(element.clientHeight - rest) < 1;
}
It checks if element is scrolled to bottom with ±1 accuracy.
This Function triggers when ever you scroll , then you can do something that you want to do
window.onscroll = function() {scrolled()};
function scrolled() {
fixed = document.body.scrollTop > 20 ||
document.documentElement.scrollTop > 20 ?
"Am Going Down" : "Am Going Up"
}
I have a product listing on my website and when hovering over the product an info div shows up. The only problem is that in some cases parts of the div fall outside of the browser window.
I need to check with Javascript if thats the case, and if so I need to assign a class to that particular div. I know how to do the last part, but I have no idea how to check if the whole div is visible.
Can anybody give me a suggestion how to handle this?
Your goal is to determine if your HTML element is in the viewport. If you're using jQuery - there are a few plugins tha handle this.
Jquery check if element is visible in viewport
http://opensource.teamdf.com/visible/examples/demo-basic.html
With the example above, you'd want to use detectPartial set to true -- so that you would know whether or not the thing is inside the viewport entirely.
//added by JG 3/10/2011, to extend selectors.
// Example:
//if ($('#whatever').is(':inView') ) {...}
jQuery.extend(jQuery.expr[':'], {
inView: function(a) {
var st = (document.documentElement.scrollTop || document.body.scrollTop),
ot = jQuery(a).offset().top,
wh = (window.innerHeight && window.innerHeight < jQuery(window).height()) ? window.innerHeight : jQuery(window).height();
return ot > st && (jQuery(a).height() + ot) < (st + wh);
}
});
I did that a number of years ago, based off of Remy Sharp's inview plugin (https://remysharp.com/2009/01/26/element-in-view-event-plugin) -- but these only check for vertical in-view, not horizontal (scrolling sideways/off the left or right).
Here's a couple of ways to ask this question:
How can I get the height (in pixels) at which the page will start having scroll bars? In other words, how do i get the window height at which a scroll bar will appear?
How can I get the maximum height of all elements on the page that don't have relative
heights (e.g. height: 100%)?
This question is related, but the answer doesn't do what I want in the case of relative heights: Finding the full height of the content of a page/document that can have absolutely positioned elements
I made a js fiddle of what I'm talking about: http://tinyurl.com/kgf8dae . Unfortunately, jsfiddle seems to break the relative height put on div e - run it as an html page in a normal browser to see the real behavior.
I might be misunderstand the question. In general, if the window height is less than the document height you will get a vertical scrollbar.
So in jQuery the check might look like this:
if( $(document).height() > $(window).height() ){ /* There will be a scrollbar */ }
You can perform this check within DOM changing and window resizing events to ascertain if a scrollbar has appeared. To preemptively determine if an event would cause a scrollbar to appear can be tricky and would likely require some understanding of the page and potential events to handle efficiently.
This is tagged through jQuery so I'm going to use jQuery; even though it's not mentioned in the question body.
a) It sounds like you want to get the height of the viewport (window); which can be retrieved like this:
var height = $(window).height();
If the height of the document (page) exceeds the height of the window, and there are no CSS properties blocking the display of scrollbars, then scrollbars will indeed by visible.
if( $(document).height() > $(window).height() )
b) This is going to be a bit trickier, in the sense the only way off the top of my head is to query every DOM element.. this is not a elegant solution; and in fact I'd ask you to reconsider your approach if you really you must do this. That said.. for curiosity...
If you're looking for the max height, in the sense of the largest element - then this would work:
// Get height of largest element.
var max_height = 0;
$('*').each( function(){
// skip <html> and <body>
if( ( $(this).get(0) == $('body').get(0) ) || ( $(this).get(0) == $('html').get(0) ) )
return;
var current_height = $(this).height();
if( current_height > max_height )
max_height = current_height;
});
For example, running that on this page...
> console.log( max_height );
570
However, I'm not sure if you want the maximum height of all combined elements.. In which case we obviously need to add all the elements up, but there's the obvious problem: elements are nested!
If this is what you want, then by using .children() we can just iterate through the lengths of the elements that are immediate children of your containing element/body.
// Get height of all combined elements
var combined_height = 0;
$('body').each( function(){ // replace with containing element?
combined_height = combined_height + jQuery(this).height();
});
For example, running that on this page:
> console.log(combined_height);
2176
Using the HTML/CSS from the example your provided via (jsfiddle.net/RMe3n/1). The answer is and always will be 242.
However, I assume you're looking for a more dynamic approach. Running the following after DOM ready will also produce 242:
var answer = 0;
$('#absolutes > div').each(function(){
var h = $(this).outerHeight(true);
if(answer < h) answer = h;
})
alert(answer);
While the above will solve for the particular HTML/CSS you provided it makes a lot of assumptions about the page's HTML structure and CSS.
Is it possible that the problem you are attempting to address with JS could be resolved in a "cleaner" way by adjusting the HTML/CSS of your page?
If you are looking for a fool proof JS method to account for ALL the multitude of unique layouts/styles that exist now and may exist as more CSS3 display types are adopted in the future I believe you're out of luck. There is no recommendable, consistent, efficient way to do so.
Note: If this is more than just a theoretical discussion, consider being more specific about the exact scenario you are faced with as there is likely a vastly different approach that may resolve the issue.
I'm trying to use the left variable to replace '1493' in this code. It works fine when it's a number but when I changed it over to use 'left' the if statement stops working.
$(document).scroll(function () {
var width = $(document).width();
var left = $(document).scrollLeft();
var postCount = $(".post").length;
var columnLength = ( width - ((postCount*743) - 1493)) - (width-(postCount*743));
if(left >= columnLength) {
$(".num").text(left);
}
});
Does anyone have any ideas where I'm going wrong with this? Any pointers would be great.
You may need to force it to be an integer:
var left = parseInt($(document).scrollLeft());
Lets take a look at the math you have really quick.
var columnLength = ( width - ((postCount*743) - 1493)) - (width-(postCount*743));
You are basically cancelling out width, and (postCount*743). It leaves you with --1493 which is positive 1493. The following would have the same effect:
var columnLength = 1493;
So, the reason the if statement fires when you put in the static value 1493, is because columnLength ALWAYS equals 1493 which, of course satisfies this condition:
if (1493 >= columnLength)
You could as easily write:
if (1493 >= 1493)
That said, it should still, theoretically fire when left becomes greater than or equal to 1493. But left is the current horizontal scroll position in pixels. It would be a HUGELY wide page to hit a scroll position of 1493.
Edit: Here's a fiddle to give an idea of how fast the scroll position increases: http://jsfiddle.net/vdQ7B/16/
EDIT 2:
Here is an update in response to your comment.
As I understand it, you were trying to get a horizontal scrollbar that would, essentially, scroll forever.
Please see the following fiddle for a demo: http://jsfiddle.net/vdQ7B/40/
The code is below:
$(document).scroll(function () {
var width = $(document).width();
var left = $(document).scrollLeft();
var viewportwidth = window.innerWidth;
// If our scrollbar gets to the end,
// add 50 more pixels. This could be set
// to anything.
if((left + viewportwidth) === width) {
$("body").css("width", width + 50);
}
});
Per the comments in the code, we simply increase the width of the body if we determine we've reached the end. scrollLeft() will only tell us the number of pixels that are currently not visible to the left of the viewable area. So, we need to know how much viewable area we have, and how much is hidden to the left to know if we've scrolled all the way to the end.
If you have a scroll bar on an inner element, like a div, you'd need to update with width of the div, not the body.
Note: You may also need to use $(window) instead of $(document) to get scrollLeft() to work across all browsers.
Note: See here about using "innerWidth". There are some compatibility issues, and you may need to expand it a bit to handle other cases (IE6).
I just want some simple JQ/JS to check if the current page/window (not a particular element) has a vertical scrollbar.
Googling gives me stuff that seems overly complex for just this basic feature.
How can this be done?
$(document).ready(function() {
// Check if body height is higher than window height :)
if ($("body").height() > $(window).height()) {
alert("Vertical Scrollbar! D:");
}
// Check if body width is higher than window width :)
if ($("body").width() > $(window).width()) {
alert("Horizontal Scrollbar! D:<");
}
});
try this:
var hasVScroll = document.body.scrollHeight > document.body.clientHeight;
This will only tell you if the vertical scrollHeight is bigger than the height of the viewable content, however. The hasVScroll variable will contain true or false.
If you need to do a more thorough check, add the following to the code above:
// Get the computed style of the body element
var cStyle = document.body.currentStyle||window.getComputedStyle(document.body, "");
// Check the overflow and overflowY properties for "auto" and "visible" values
hasVScroll = cStyle.overflow == "visible"
|| cStyle.overflowY == "visible"
|| (hasVScroll && cStyle.overflow == "auto")
|| (hasVScroll && cStyle.overflowY == "auto");
I tried the previous answer and doesn't seem to be working the $("body").height() does not necessarily represent the whole html height.
I have corrected the solution as follows:
// Check if body height is higher than window height :)
if ($(document).height() > $(window).height()) {
alert("Vertical Scrollbar! D:");
}
// Check if body width is higher than window width :)
if ($(document).width() > $(window).width()) {
alert("Horizontal Scrollbar! D:<");
}
Let's bring this question back from the dead ;) There is a reason Google doesn't give you a simple solution. Special cases and browser quirks affect the calculation, and it is not as trivial as it seems to be.
Unfortunately, there are problems with the solutions outlined here so far. I don't mean to disparage them at all - they are great starting points and touch on all the key properties needed for a more robust approach. But I wouldn't recommend copying and pasting the code from any of the other answers because
they don't capture the effect of positioned content in a way that is reliable cross-browser. The answers which are based on body size miss this entirely (the body is not the offset parent of such content unless it is positioned itself). And those answers checking $( document ).width() and .height() fall prey to jQuery's buggy detection of document size.
Relying on window.innerWidth, if the browser supports it, makes your code fail to detect scroll bars in mobile browsers, where the width of the scroll bar is generally 0. They are just shown temporarily as an overlay and don't take up space in the document. Zooming on mobile also becomes a problem that way (long story).
The detection can be thrown off when people explicitly set the overflow of both the html and body element to non-default values (what happens then is a little involved - see this description).
In most answers, body padding, borders or margins are not detected and distort the results.
I have spent more time than I would have imagined on a finding a solution that "just works" (cough). The algorithm I have come up with is now part of a plugin, jQuery.isInView, which exposes a .hasScrollbar method. Have a look at the source if you wish.
In a scenario where you are in full control of the page and don't have to deal with unknown CSS, using a plugin may be overkill - after all, you know which edge cases apply, and which don't. However, if you need reliable results in an unknown environment, then I don't think the solutions outlined here will be enough. You are better off using a well-tested plugin - mine or anybody elses.
This one did works for me:
function hasVerticalScroll(node){
if(node == undefined){
if(window.innerHeight){
return document.body.offsetHeight> window.innerHeight;
}
else {
return document.documentElement.scrollHeight >
document.documentElement.offsetHeight ||
document.body.scrollHeight>document.body.offsetHeight;
}
}
else {
return node.scrollHeight> node.offsetHeight;
}
}
For the body, just use hasVerticalScroll().
let hasScrollbar = window.innerWidth > document.documentElement.clientWidth;
Oddly none of these solutions tell you if a page has a vertical scrollbar.
window.innerWidth - document.body.clientWidth will give you the width of the scrollbar. This should work in anything IE9+ (not tested in the lesser browsers). (Or to strictly answer the question, !!(window.innerWidth - document.body.clientWidth)
Why? Let's say you have a page where the content is taller than the window height and the user can scroll up/down. If you're using Chrome on a Mac with no mouse plugged in, the user will not see a scrollbar. Plug a mouse in and a scrollbar will appear. (Note this behaviour can be overridden, but that's the default AFAIK).
<script>
var scrollHeight = document.body.scrollHeight;
var clientHeight = document.documentElement.clientHeight;
var hasVerticalScrollbar = scrollHeight > clientHeight;
alert(scrollHeight + " and " + clientHeight); //for checking / debugging.
alert("hasVerticalScrollbar is " + hasVerticalScrollbar + "."); //for checking / debugging.
</script>
This one will tell you if you have a scrollbar or not. I've included some information that may help with debugging, which will display as a JavaScript alert.
Put this in a script tag, after the closing body tag.
I found vanila solution
var hasScrollbar = function() {
// The Modern solution
if (typeof window.innerWidth === 'number')
return window.innerWidth > document.documentElement.clientWidth
// rootElem for quirksmode
var rootElem = document.documentElement || document.body
// Check overflow style property on body for fauxscrollbars
var overflowStyle
if (typeof rootElem.currentStyle !== 'undefined')
overflowStyle = rootElem.currentStyle.overflow
overflowStyle = overflowStyle || window.getComputedStyle(rootElem, '').overflow
// Also need to check the Y axis overflow
var overflowYStyle
if (typeof rootElem.currentStyle !== 'undefined')
overflowYStyle = rootElem.currentStyle.overflowY
overflowYStyle = overflowYStyle || window.getComputedStyle(rootElem, '').overflowY
var contentOverflows = rootElem.scrollHeight > rootElem.clientHeight
var overflowShown = /^(visible|auto)$/.test(overflowStyle) || /^(visible|auto)$/.test(overflowYStyle)
var alwaysShowScroll = overflowStyle === 'scroll' || overflowYStyle === 'scroll'
return (contentOverflows && overflowShown) || (alwaysShowScroll)
}
I use
function windowHasScroll()
{
return document.body.clientHeight > document.documentElement.clientHeight;
}
Simply compare the width of the documents root element (i.e. html element) against the inner portion of the window:
if ((window.innerWidth - document.documentElement.clientWidth) >0) console.log('V-scrollbar active')
If you also need to know the scrollbar width:
vScrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
Other solutions didn't work in one of my projects and I've ending up checking overflow css property
function haveScrollbar() {
var style = window.getComputedStyle(document.body);
return style["overflow-y"] != "hidden";
}
but it will only work if scrollbar appear disappear by changing the prop it will not work if the content is equal or smaller than the window.
I wrote an updated version of Kees C. Bakker's answer:
const hasVerticalScroll = (node) => {
if (!node) {
if (window.innerHeight) {
return document.body.offsetHeight > window.innerHeight
}
return (document.documentElement.scrollHeight > document.documentElement.offsetHeight)
|| (document.body.scrollHeight > document.body.offsetHeight)
}
return node.scrollHeight > node.offsetHeight
}
if (hasVerticalScroll(document.querySelector('body'))) {
this.props.handleDisableDownScrollerButton()
}
The function returns true or false depending whether the page has a vertical scrollbar or not.
For example:
const hasVScroll = hasVerticalScroll(document.querySelector('body'))
if (hasVScroll) {
console.log('HAS SCROLL', hasVScroll)
}