Detect if a page has a vertical scrollbar? - javascript

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

Related

A method to determined can Scroll in X or Y [duplicate]

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

Check if a user has scrolled to the bottom in subpixel precision era

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

Javascript positioning not working in Chrome or Safari

I have a script going in Javascript, the purpose of which is to make an image stay centered in the window when the window is smaller than the image. It just moves the image to the left by half the difference between the image width and the window width so that the center of the image is always the center of the screen. When the window is not smaller than the image, this left offset is set to zero. And it works perfectly, if I'm in IE or Firefox. On the webkit browsers, it doesn't ever go to zero, creating an effect akin to float:right when the window is wider than the image. Here's the code:
setTimeout(slideImageWidth, 1);
function slideImageWidth() {
var slideWidth = window.getComputedStyle(document.getElementById("slide-image")).width,
windowWidth = window.innerWidth,
slide = document.getElementById("slide-image"),
slideWidth = window.getComputedStyle(slide).width;
if (windowWidth < (slideWidth.replace("px", "") + 1) && slide.style.display !== "none") {
slide.style.left = ((((-slideWidth.replace("px", "") + windowWidth)) / 2) + "px");
}
else {
slide.style.left = 0;
setTimeout(slideImageWidth, 1);
};
I tried putting slide.style.left = 0 before the if and just letting the loop take care of it in the next millisecond, but that didn't work either. I've also tried both placements with:
slide.style.left = "0px";
slide.style.left = 0 + "px";
slide.style.left = "0" + "px";
slide.style.left = 0px;
none of which worked in Chrome or Safari, but all but the last of which worked in Firefox and IE.
When I use alert(slide.style.left) when the window is wider than the image, a positive value is returned in Chrome and Safari, unlike the 0 from Firefox and IE, which tells me that the 0 value is never being written to slide.style.left. Yet, I know that I can modify slide.style.left because it still positions itself based on the equation.
Why does this code not work with the webkit browsers, and how can I fix it?
First, the things which I think are accidental typos:
1) Your code references something called "slide1Width", which is defined nowhere.
2) Your code references something called "slide1", which is defined nowhere.
3) You don't have the right number of close brackets, and the last bracket inexplicably has a semicolon after it.
Second, the most obvious error that isn't causing your specific problem:
(slideWidth.replace("px", "") + 1)
This expression is not what you want. If slideWidth is "440px", the replace() call gives you "440", and ("440" + 1) is the string "4441". I don't think that's what you mean to do here.
Third, and finally, what I believe is the cause of the actual bug you're asking about: timing. If you open up the dev tools and manually run slideImageWidth() on a wide window after it has loaded and failed to center itself, the image will in fact jump to the center, even on Chrome. Why doesn't it do it on page load? Here:
window.getComputedStyle(slide).width
That expression returns "0px" right when the page is first loaded. If you wait until the image is done loading, you'll be able to get an actual width for it, in which case you can do the calculations you want. (Or, presumably, you could set the width yourself via styling.) It seems that IE is getting the image loaded and flowed before running the script, whereas Chrome is not.
Is there a reason you want to use setTimeout instead of a resize event? What about something like this:
window.onresize = function(event) {
setSlidePosition();
};
function setSlidePosition() {
var slideLeft = (document.getElementById("slide-image").style.left.indexOf('px') != -1 ) ? document.getElementById("slide-image").style.left : '0px';
var numLeft = slideLeft.replace('px','');
console.log(numLeft);
var element = document.getElementById("slide-image")
if(element.width > window.innerWidth) {
var newLeft = (element.width - window.innerWidth) / 2 * -1 + "px";
document.getElementById("slide-image").style.left = newLeft;
} else {
document.getElementById("slide-image").style.left = "0px";
}
};
setSlidePosition();
http://jsfiddle.net/4qomq7tb/45/
Seems to behave the same in chrome and FF at least. This doesn't specifically answer the question relating relating to your code, though :/

Check if whole div is visible in browser window

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

Confused by document dimensions in JavaScript

I am really confused by the different properties that exist in JavaScript for getting the dimensions of a document and how to get those numbers. Can someone recommend a good place to start to understand how I would get the size of the document and position stuff correctly?
I'll try to answer as simply as I can.
The Document and Viewport
In terms of geometry, there are two sets of dimensions to be aware of; the document dimensions, which reflect the entire size of the loaded page, including the content beyond the bottom of the window and the viewport dimensions, which reflect the size of the visible part of the document that is immediately displayed in the window.
When you scroll down, the viewport moves down over the document by a certain number of pixels. In other words, the viewport is the actual browser window "border" (the toolbars, menus, tabs, and so on).
The confusion comes from the fact that depending on the browser and mode, different properties are used to get the dimensions of a document and viewport, and they return different results depending on scrollbars. But we'll come back to this.
Dimensions Overview
There are a number of properties available to you from the get-go in javascript which give you different dimensions.
Screen resolution:
window.screen.width -Height
Available screen space (same as monitor resolution) minus docks, toolbars and other UI elements:
window.screen.availWidth -Height.
Document dimensions:
document.documentElement.offsetWidth -Height
Note: These numbers do not include the scrollbars.
Viewport dimensions:
window.innerWidth -Height
These numbers include the scrollbars.
This is not available in IE 8- and IE9, so if IE, test for the document.compatMode === "CSS1Compat" and if true, use document.documentElement.clientWidth -Height, and for quirks mode use document.body.clientWidth -Height.
A note about document dimensions
As per above, document.documentElement.offsetWidth/Height provides you with the actual size of the document. One caveat to this is that scrollbars work differently between browsers. For example, IE9 will always display a vertical scrollbar even if the document height is less than the viewport height. Safari/Chrome doesn't have scrollbars on OS X Lion. Chrome on PC will not display vertical scrollbars unless it needs to.
So you may bump into inconsistencies and the Scrollbar shifts content problem. Imagine you have an absolutely positioned and centred element. Because CSS calculates the "centre" relative to the document dimensions and not the viewport dimensions, when say, Google adds the scrollbars, your content may "jump" a bit to the left as the "document centre" changes. So you may need to write JS to compensate for this effect if it bothers you, or maybe someone here can write a quick JS function to calculate document dimensions with scrollbars included.
Scrollbar Position and Dimensions
While some methods in JavaScript work with document coordinates, others work with viewport coordinates, and often this is not what you want. For example, if you have an element's top edge at 20px in document coordinates, and you scroll the page down by 20px, the top edge of that element will be at 0px relative to the top viewport coordinate. So to convert between the two systems, you first need to know by how many pixels a user has scrolled the document, and then add that number to the viewport to compensate (look at example below).
I also found these helpful:
http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
http://www.quirksmode.org/mobile/viewports.html
And here's a quick cross-browser module I mucked up to help you:
var dimensions = (function(){
var dims = {};
// get screen width/height:
dims.screenWidth = function() { window.screen.width };
dims.screenHeight = function() { return window.screen.height };
// get screen width/height minus chrome:
dims.availWidth = function() { return window.screen.availWidth };
dims.availHeight = function() { return window.screen.availHeight };
// get document width/height (with-out scrollbars):
if (window.document.compatMode == "CSS1Compat"){ // if IE Standards Mode
dims.documentWidth = function() { return document.body.offsetWidth };
dims.documentHeight = function() { return document.body.offsetHeight };
}
else {
dims.documentWidth = function() { return document.documentElement.offsetWidth };
dims.documentHeight = function() { return document.documentElement.offsetHeight };
}
// get viewport width/height (with scrollbars):
if (window.innerWidth != null) {
dims.viewportWidth = function () { return window.innerWidth };
dims.viewportHeight = function () { return window.innerHeight };
}
// if IE in Standards Mode
else if (window.document.compatMode == "CSS1Compat"){
dims.viewportWidth = function () {
return window.document.documentElement.clientWidth
};
dims.viewportHeight = function () {
return window.document.documentElement.clientHeight
};
}
// get scrollbar offsets:
if (window.pageXOffset != null) {
dims.scrollXOffset = function() { return window.pageXOffset };
dims.scrollYOffset = function() { return window.pageYOffset };
}
// if IE in Standards Mode
else if (window.document.compatMode == "CSS1Compat"){
dims.scrollXOffset = function() { return document.documentElement.scrollLeft };
dims.scrollYOffset = function() { return document.documentElement.scrollTop };
}
return dims;
}());
You can for example do console.log(dimensions.viewportWidth()) to get the viewport width.
Hope this helps you :)

Categories

Resources