How to find out which element is causing a scrollbar - javascript

I have a certain question which goes into kind of debugging. I want you to ask, how I could find out which element of the whole markup is causing a scrollbar. Any kind of method would be fine.
I thought about searching for overflow in developer tools, but this doesn't really help me.
Does anyone know how I could solve this?

You will want to check a few things. First, that an element has an overflow that would produce a scrollbar: overflow: scroll forces them and overflow: auto will display them if necessary. In the case that the overflows are auto, you can then check it's scroll height against it's actual height.
function isScroller(el) {
var isScrollableWidth, isScollableHeight, elStyle;
elStyle = window.getComputedStyle(el, null); // Note: IE9+
if (elStyle.overflow === 'scroll' ||
elStyle.overflowX === 'scroll' ||
elStyle.overflowY === 'scroll') {
return true;
}
if (elStyle.overflow === 'auto' ||
elStyle.overflowX === 'auto' ||
elStyle.overflowY === 'auto') {
if (el.scrollHeight > el.clientHeight) {
return true;
}
if (el.scrollWidth > el.clientWidth) {
return true;
}
}
return false;
}
var els = document.querySelectorAll('body *');
for (var i = 0, el; el = els[i]; i++) {
if (isScroller(el)) {
console.log(el);
}
}
You can see it here (open your console): http://jsfiddle.net/rgthree/zfyhby1j/
Note, that some touch devices may not produce an actual "scrollbar" except when scrolling. This won't detect that but, rather, that the element is capable of scrolling.

Related

Detecting the opening or closing of a virtual keyboard on a touchscreen device

I have an inelegant workaround for this issue, and am hoping that others may already have more robust solutions.
On a touchscreen, tapping on an editable text field will bring up an on-screen keyboard, and this will change the amount of screen space available. Left untreated, this may hide key elements, or push a footer out of place.
On a laptop or desktop computer, opening an editable text field creates no such layout changes.
In my current project, I want to ensure that certain key items are visible even when a virtual keyboard is open, so I need to detect when such a change occurs. I can then add a class to the body element, to change the layout to suit the presence of the keyboard.
When searching for existing solutions online, I discovered that:
There is no perfect way of knowing that your code is running on a mobile device
There are non-mobile devices that have touchscreens, and which may also have keyboards
A focus element may not be editable
contentEditable elements will open the on-screen keyboard
The address bar may decide to reappear and take up essential screen space at the same time the virtual keyboard appears, squeezing the available space even more.
I have posted the solution that I have come up with below. It relies on detecting a change in height of the window within a second of the keyboard focus changing. I am hoping that you might have a better solution to propose that has been tested cross-platform, cross-browser and across devices.
I've created a repository on GitHub.
You can test my solution here.
In my tests, this may give a false positive if the user is using a computer with a touchscreen and a keyboard and mouse, and uses the mouse first to (de-)select an editable element and then immediately changes the window height. If you find other false positives or negatives, either on a computer or a mobile device, please let me know.
;(function (){
class Keyboard {
constructor () {
this.screenWidth = screen.width // detect orientation
this.windowHeight = window.innerHeight // detect keyboard change
this.listeners = {
resize: []
, keyboardchange: []
, focuschange: []
}
this.isTouchScreen = 'ontouchstart' in document.documentElement
this.focusElement = null
this.changeFocusTime = new Date().getTime()
this.focusDelay = 1000 // at least 600 ms is required
let focuschange = this.focuschange.bind(this)
document.addEventListener("focus", focuschange, true)
document.addEventListener("blur", focuschange, true)
window.onresize = this.resizeWindow.bind(this)
}
focuschange(event) {
let target = event.target
let elementType = null
let checkType = false
let checkEnabled = false
let checkEditable = true
if (event.type === "focus") {
elementType = target.nodeName
this.focusElement = target
switch (elementType) {
case "INPUT":
checkType = true
case "TEXTAREA":
checkEditable = false
checkEnabled = true
break
}
if (checkType) {
let type = target.type
switch (type) {
case "color":
case "checkbox":
case "radio":
case "date":
case "file":
case "month":
case "time":
this.focusElement = null
checkEnabled = false
default:
elementType += "[type=" + type +"]"
}
}
if (checkEnabled) {
if (target.disabled) {
elementType += " (disabled)"
this.focusElement = null
}
}
if (checkEditable) {
if (!target.contentEditable) {
elementType = null
this.focusElement = null
}
}
} else {
this.focusElement = null
}
this.changeFocusTime = new Date().getTime()
this.listeners.focuschange.forEach(listener => {
listener(this.focusElement, elementType)
})
}
resizeWindow() {
let screenWidth = screen.width;
let windowHeight = window.innerHeight
let dimensions = {
width: innerWidth
, height: windowHeight
}
let orientation = (screenWidth > screen.height)
? "landscape"
: "portrait"
let focusAge = new Date().getTime() - this.changeFocusTime
let closed = !this.focusElement
&& (focusAge < this.focusDelay)
&& (this.windowHeight < windowHeight)
let opened = this.focusElement
&& (focusAge < this.focusDelay)
&& (this.windowHeight > windowHeight)
if ((this.screenWidth === screenWidth) && this.isTouchScreen) {
// No change of orientation
// opened or closed can only be true if height has changed.
//
// Edge case
// * Will give a false positive for keyboard change.
// * The user has a tablet computer with both screen and
// keyboard, and has just clicked into or out of an
// editable area, and also changed the window height in
// the appropriate direction, all with the mouse.
if (opened) {
this.keyboardchange("shown", dimensions)
} else if (closed) {
this.keyboardchange("hidden", dimensions)
} else {
// Assume this is a desktop touchscreen computer with
// resizable windows
this.resize(dimensions, orientation)
}
} else {
// Orientation has changed
this.resize(dimensions, orientation)
}
this.windowHeight = windowHeight
this.screenWidth = screenWidth
}
keyboardchange(change, dimensions) {
this.listeners.keyboardchange.forEach(listener => {
listener(change, dimensions)
})
}
resize(dimensions, orientation) {
this.listeners.resize.forEach(listener => {
listener(dimensions, orientation)
})
}
addEventListener(eventName, listener) {
// log("*addEventListener " + eventName)
let listeners = this.listeners[eventName] || []
if (listeners.indexOf(listener) < 0) {
listeners.push(listener)
}
}
removeEventListener(eventName, listener) {
let listeners = this.listeners[eventName] || []
let index = listeners.indexOf(listener)
if (index < 0) {
} else {
listeners.slice(index, 1)
}
}
}
window.keyboard = new Keyboard()
})()
There is a new experimental API that is meant exactly to track size changes due to the keyboard appearing and other mobile weirdness like that.
window.visualViewport
https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API
By listening to resize events and comparing the height to the height to the so called "layout viewport". See that it changed by a significant amount, like maybe 30 pixels. You might deduce something like "the keyboard is showing".
if('visualViewport' in window) {
window.visualViewport.addEventListener('resize', function(event) {
if(event.target.height + 30 < document.scrollElement.clientHeight) {
console.log("keyboard up?");
} else {
console.log("keyboard down?");
}
});
}
(code above is untested and I suspect zooming might trigger false positive, might have to check for scaling changes as well)
As no direct way to detect the keyboard opening, you can only detect by the height and width. See more
In javascript screen.availHeight and screen.availWidth maybe help.
Using visualViewPort
This was inspired by on-screen-keyboard-detector. It works on Android and iOS.
if ('visualViewport' in window) {
const VIEWPORT_VS_CLIENT_HEIGHT_RATIO = 0.75;
window.visualViewport.addEventListener('resize', function (event) {
if (
(event.target.height * event.target.scale) / window.screen.height <
VIEWPORT_VS_CLIENT_HEIGHT_RATIO
)
console.log('keyboard is shown');
else console.log('keyboard is hidden');
});
}
Another approach using virtualKeyboard
This worked, but isn't supported in iOS yet.
if ('virtualKeyboard' in navigator) {
// Tell the browser you are taking care of virtual keyboard occlusions yourself.
navigator.virtualKeyboard.overlaysContent = true;
navigator.virtualKeyboard.addEventListener('geometrychange', (event) => {
const { x, y, width, height } = event.target.boundingRect;
if (height > 0) console.log('keyboard is shown');
else console.log('keyboard is hidden');
});
Source: https://developer.chrome.com/docs/web-platform/virtual-keyboard/
This is a difficult problem to get 'right'. You can try and hide the footer on input element focus, and show on blur, but that isn't always reliable on iOS. Every so often (one time in ten, say, on my iPhone 4S) the focus event seems to fail to fire (or maybe there is a race condition with JQuery Mobile), and the footer does not get hidden.
After much trial and error, I came up with this interesting solution:
<head>
...various JS and CSS imports...
<script type="text/javascript">
document.write( '<style>#footer{visibility:hidden}#media(min-height:' + ($( window ).height() - 10) + 'px){#footer{visibility:visible}}</style>' );
</script>
</head>
Essentially: use JavaScript to determine the window height of the device, then dynamically create a CSS media query to hide the footer when the height of the window shrinks by 10 pixels. Because opening the keyboard resizes the browser display, this never fails on iOS. Because it's using the CSS engine rather than JavaScript, it's much faster and smoother too!
Note: I found using 'visibility:hidden' less glitchy than 'display:none' or 'position:static', but your mileage may vary.
I'm detecting the visibility of a virtual keyboard as follows:
window.addEventListener('resize', (event) => {
// if current/available height ratio is small enough, virtual keyboard is probably visible
const isKeyboardHidden = ((window.innerHeight / window.screen.availHeight) > 0.6);
});

Hide div when reached element show when passed

I'm looking for ideas to hide a div when it reaches another div and show it again when it passes the same div. There can also be multiple divs to pass. Something like:
var bottom = $('.out-of-grid-images')
$(window).scroll(function(){
if ($(this).scrollTop() > bottom){
$('.share-icons').fadeOut(200); // hide share icons when reached and overlaps
}
else if ($(this).scrollTop() < bottom){ {
$('.share-icons').fadeIn(200); // show icons when passed element
}
});
I can not produce a jsFiddle because I haven't found anything similar to work with. Any ideas?
EDIT:
The share icons are a fixed position element. The elements reached are dynamic and not fixed from top of the page (they are post content images out of the grid)
EDIT: here's an image to illustrate the matter
What you'll want to do is have an Array of divs that you want to trigger the hiding effect, then calculate their bounding rectangle, and then perform collision detection on them.
Here is a very rough example - you'll have to fix the showing and hiding, as this will cause a fadeIn/fadeOut on each scroll event.
const shareIcons = $('.share-icons');
const divs = $('.trigger-div');
const rects = [];
divs.each(function () {
rects.push(this.getBoundingClientRect());
});
$(window).scroll(function () {
let interectsAny = false;
rects.forEach(function (rect) {
if (intersectRect(rect, shareIcons[0].getBoundingClientRect())) {
interectsAny = true;
}
});
if (interectsAny) {
shareIcons.fadeOut(200);
} else {
shareIcons.fadeIn(200);
}
});
// https://stackoverflow.com/questions/2752349/fast-rectangle-to-rectangle-intersection
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}

Get and set top position

I am using hammer.js to make a basic touch friendly mobile site. I have hidden the top menu with a negative top position and it will appear using css3 transitions upon a double tap. However I want it to then hide again upon a second double tap.
I have tried using if/else to call the top position then re-set it accordingly but I cant get it to work, anyone know where I am going wrong?
var t = $("#topbar");
var position = t.position();
$sw.on('doubletap', function(event) {
event.preventDefault();
if (position.top == '-55px') {
$('#topbar').css("top", "0");
}
else {
$('#topbar').css("top", "-55px")
}
});
The site address is http://www.bettondesignwork.co.uk/tim/mobile
Thanks
You can try this:
var t = $("#topbar");
var position = t.offset();
$sw.on('doubletap', function(event) {
event.preventDefault();
if (position.top == '-55px') {
$("#topbar").offset({ top: "0"});
}
else {
$('#topbar').offset({ top: "-55px"});
}
}​
The problem could be this line
if (position.top == '-55px')
If i'm not mistaken, the .top property is numeric, so u could change the code to
if (position.top == -55)
Or become more defensive
if (position.top < 0)
Gets top position of one HTMLElement
function getHTMLElementTop(thisNode, Top)
{
if (Top == undefined)
{
var Top = 0;
}
if (thisNode.nodeType == 1)
{
Top += thisNode.offsetTop;
//if (thisNode.parentNode)
if (thisNode.offsetParent)
{
Top = getHTMLElementTop(thisNode.offsetParent, Top);
}
}
return Top;
}
May be it helps

IE7/IE8 takes some time to return the correct scroll position

For displaying a dialog, I need to know the current scroll position of the window. This is done within a $(document).ready block of jQuery.
Internet Explorer, however, breaks things when having to remember the scroll position.
You can try this by opening the following HTML code in IE, scroll down a bit and hit reload (sorry I didn't create a fiddle – because of the scrolling it would not work there).
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"
type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
alert(document.body.scrollTop);
alert(document.body.scrollTop);
});
</script>
<div style="height:4000px;">
</div>
The first alert shows 0, the second one shows the correct number. It also works without the first alert if I use setTimeout and wait for (in my case) 20msecs - which of couse might take another amount of time in other circumstances and therefore is no solution.
I think the problem is that IE first scrolls top, does some things, and then, after a while, restores the previous scrolling position (probably it has to render things first in order to make sure that this scroll position still exists at all).
Is there any propper way of getting the browser scroll right at once?
There are several properties to get page scroll, and it's different in every browser.
Here is a function I wrote in order to receive the correct scroll:
function getScroll(type)
{
type = type || 'top';
var result = 0;
var scroll = 0;
if (type === 'top')
{
if (ISQ.Http.browser.app !== 'ie' || ISQ.Http.browser.isIE9)
scroll = window.pageYOffset;
if (ISQ.Http.browser.app !== 'ie')
scroll = returnMaxScroll(scroll, window.scrollY);
scroll = returnMaxScroll(scroll, document.body.scrollTop);
scroll = returnMaxScroll(scroll, document.documentElement.scrollTop);
scroll = returnMaxScroll(scroll, document.body.parentNode.scrollTop);
}
else if (type === 'left')
{
if (ISQ.Http.browser.app !== 'ie' || ISQ.Http.browser.isIE9)
scroll = window.pageXOffset;
if (ISQ.Http.browser.app !== 'ie')
scroll = returnMaxScroll(scroll, window.scrollX);
scroll = returnMaxScroll(scroll, document.body.scrollLeft);
scroll = returnMaxScroll(scroll, document.documentElement.scrollLeft);
scroll = returnMaxScroll(scroll, document.body.parentNode.scrollLeft);
}
else if (type === 'height')
{
switch (ISQ.Http.browser.app)
{
case "chrome":
case "ff":
case "opera":
case "safari":
// iframe
if (window.top === window)
scroll = document.documentElement.scrollHeight;
// top window
else
scroll = document.body.scrollHeight;
break;
default:
scroll = returnMaxScroll(scroll, document.body.scrollHeight);
scroll = returnMaxScroll(scroll, document.documentElement.scrollHeight);
scroll = returnMaxScroll(scroll, document.body.parentNode.scrollHeight);
break;
}
}
else if (type === 'width')
{
scroll = returnMaxScroll(scroll, document.body.scrollWidth);
scroll = returnMaxScroll(scroll, document.documentElement.scrollWidth);
scroll = returnMaxScroll(scroll, document.body.parentNode.scrollWidth);
}
return scroll;
}
function returnMaxScroll(value1, value2)
{
if (value1 > value2) return value1;
return value2;
}
FYI: you may need to implement these booleans:
- ISQ.Http.browser.app !== 'ie'
- ISQ.Http.browser.isIE9
I hope it'll help you :)

How can I check if a scrollbar is visible?

Is it possible to check the overflow:auto of a div?
For example:
HTML
<div id="my_div" style="width: 100px; height:100px; overflow:auto;" class="my_class">
* content
</div>
JQUERY
$('.my_class').live('hover', function (event)
{
if (event.type == 'mouseenter')
{
if( ... if scrollbar visible ? ... )
{
alert('true'):
}
else
{
alert('false'):
}
}
});
Sometimes is the content short (no scrollbar) and sometimes long (scrollbar visible).
a little plugin for it.
(function($) {
$.fn.hasScrollBar = function() {
return this.get(0).scrollHeight > this.height();
}
})(jQuery);
use it like this,
$('#my_div1').hasScrollBar(); // returns true if there's a `vertical` scrollbar, false otherwise..
tested working on Firefox, Chrome, IE6,7,8
but not working properly on body tag selector
demo
Edit
I found out that when you have horizontal scrollbar that causes vertical scrollbar to appear, this function does not work....
I found out another solution... use clientHeight
return this.get(0).scrollHeight > this.get(0).clientHeight;
You can do this using a combination of the Element.scrollHeight and Element.clientHeight attributes.
According to MDN:
The Element.scrollHeight read-only attribute is a measurement of the height of an element's content, including content not visible on the screen due to overflow. The scrollHeight value is equal to the minimum clientHeight the element would require in order to fit all the content in the viewpoint without using a vertical scrollbar. It includes the element padding but not its margin.
And:
The Element.clientHeight read-only property returns the inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin.
clientHeight can be calculated as CSS height + CSS padding - height of horizontal scrollbar (if present).
Therefore, the element will display a scrollbar if the scroll height is greater than the client height, so the answer to your question is:
function scrollbarVisible(element) {
return element.scrollHeight > element.clientHeight;
}
Maybe a more simple solution.
if ($(document).height() > $(window).height()) {
// scrollbar
}
I should change a little thing of what Reigel said:
(function($) {
$.fn.hasScrollBar = function() {
return this[0] ? this[0].scrollHeight > this.innerHeight() : false;
}
})(jQuery);
innerHeight counts control's height and its top and bottom paddings
This expands on #Reigel's answer. It will return an answer for horizontal or vertical scrollbars.
(function($) {
$.fn.hasScrollBar = function() {
var e = this.get(0);
return {
vertical: e.scrollHeight > e.clientHeight,
horizontal: e.scrollWidth > e.clientWidth
};
}
})(jQuery);
Example:
element.hasScrollBar() // Returns { vertical: true/false, horizontal: true/false }
element.hasScrollBar().vertical // Returns true/false
element.hasScrollBar().horizontal // Returns true/false
You need element.scrollHeight. Compare it with $(element).height().
I made a new custom :pseudo selector for jQuery to test whether an item has one of the following css properties:
overflow: [scroll|auto]
overflow-x: [scroll|auto]
overflow-y: [scroll|auto]
I wanted to find the closest scrollable parent of another element so I also wrote another little jQuery plugin to find the closest parent with overflow.
This solution probably doesn't perform the best, but it does appear to work. I used it in conjunction with the $.scrollTo plugin. Sometimes I need to know whether an element is inside another scrollable container. In that case I want to scroll the parent scrollable element vs the window.
I probably should have wrapped this up in a single plugin and added the psuedo selector as a part of the plugin, as well as exposing a 'closest' method to find the closest (parent) scrollable container.
Anywho....here it is.
$.isScrollable jQuery plugin:
$.fn.isScrollable = function(){
var elem = $(this);
return (
elem.css('overflow') == 'scroll'
|| elem.css('overflow') == 'auto'
|| elem.css('overflow-x') == 'scroll'
|| elem.css('overflow-x') == 'auto'
|| elem.css('overflow-y') == 'scroll'
|| elem.css('overflow-y') == 'auto'
);
};
$(':scrollable') jQuery pseudo selector:
$.expr[":"].scrollable = function(a) {
var elem = $(a);
return elem.isScrollable();
};
$.scrollableparent() jQuery plugin:
$.fn.scrollableparent = function(){
return $(this).closest(':scrollable') || $(window); //default to $('html') instead?
};
Implementation is pretty simple
//does a specific element have overflow scroll?
var somedivIsScrollable = $(this).isScrollable();
//use :scrollable psuedo selector to find a collection of child scrollable elements
var scrollableChildren = $(this).find(':scrollable');
//use $.scrollableparent to find closest scrollable container
var scrollableparent = $(this).scrollableparent();
UPDATE: I found that Robert Koritnik already came up with a much more powerful :scrollable pseudo selector that will identify the scrollable axes and height of scrollable containers, as a part of his $.scrollintoview() jQuery plugin. scrollintoview plugin
Here is his fancy pseudo selector (props):
$.extend($.expr[":"], {
scrollable: function (element, index, meta, stack) {
var direction = converter[typeof (meta[3]) === "string" && meta[3].toLowerCase()] || converter.both;
var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle);
var overflow = {
x: scrollValue[styles.overflowX.toLowerCase()] || false,
y: scrollValue[styles.overflowY.toLowerCase()] || false,
isRoot: rootrx.test(element.nodeName)
};
// check if completely unscrollable (exclude HTML element because it's special)
if (!overflow.x && !overflow.y && !overflow.isRoot)
{
return false;
}
var size = {
height: {
scroll: element.scrollHeight,
client: element.clientHeight
},
width: {
scroll: element.scrollWidth,
client: element.clientWidth
},
// check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars
scrollableX: function () {
return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client;
},
scrollableY: function () {
return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client;
}
};
return direction.y && size.scrollableY() || direction.x && size.scrollableX();
}
});
(scrollWidth/Height - clientWidth/Height) is a good indicator for the presence of a scrollbar, but it will give you a "false positive" answer on many occasions.
if you need to be accurate i would suggest using the following function.
instead of trying to guess if the element is scrollable - you can scroll it...
function isScrollable( el ){
var y1 = el.scrollTop;
el.scrollTop += 1;
var y2 = el.scrollTop;
el.scrollTop -= 1;
var y3 = el.scrollTop;
el.scrollTop = y1;
var x1 = el.scrollLeft;
el.scrollLeft += 1;
var x2 = el.scrollLeft;
el.scrollLeft -= 1;
var x3 = el.scrollLeft;
el.scrollLeft = x1;
return {
horizontallyScrollable: x1 !== x2 || x2 !== x3,
verticallyScrollable: y1 !== y2 || y2 !== y3
}
}
function check( id ){
alert( JSON.stringify( isScrollable( document.getElementById( id ))));
}
#outer1, #outer2, #outer3 {
background-color: pink;
overflow: auto;
float: left;
}
#inner {
width: 150px;
height: 150px;
}
button { margin: 2em 0 0 1em; }
<div id="outer1" style="width: 100px; height: 100px;">
<div id="inner">
<button onclick="check('outer1')">check if<br>scrollable</button>
</div>
</div>
<div id="outer2" style="width: 200px; height: 100px;">
<div id="inner">
<button onclick="check('outer2')">check if<br>scrollable</button>
</div>
</div>
<div id="outer3" style="width: 100px; height: 180px;">
<div id="inner">
<button onclick="check('outer3')">check if<br>scrollable</button>
</div>
</div>
Ugh everyone's answers on here are incomplete, and lets stop using jquery in SO answers already please. Check jquery's documentation if you want info on jquery.
Here's a generalized pure-javascript function for testing whether or not an element has scrollbars in a complete way:
// dimension - Either 'y' or 'x'
// computedStyles - (Optional) Pass in the domNodes computed styles if you already have it (since I hear its somewhat expensive)
function hasScrollBars(domNode, dimension, computedStyles) {
dimension = dimension.toUpperCase()
if(dimension === 'Y') {
var length = 'Height'
} else {
var length = 'Width'
}
var scrollLength = 'scroll'+length
var clientLength = 'client'+length
var overflowDimension = 'overflow'+dimension
var hasVScroll = domNode[scrollLength] > domNode[clientLength]
// Check the overflow and overflowY properties for "auto" and "visible" values
var cStyle = computedStyles || getComputedStyle(domNode)
return hasVScroll && (cStyle[overflowDimension] == "visible"
|| cStyle[overflowDimension] == "auto"
)
|| cStyle[overflowDimension] == "scroll"
}
The first solution above works only in IE
The second solution above works only in FF
This combination of both functions works in both browsers:
//Firefox Only!!
if ($(document).height() > $(window).height()) {
// has scrollbar
$("#mtc").addClass("AdjustOverflowWidth");
alert('scrollbar present - Firefox');
} else {
$("#mtc").removeClass("AdjustOverflowWidth");
}
//Internet Explorer Only!!
(function($) {
$.fn.hasScrollBar = function() {
return this.get(0).scrollHeight > this.innerHeight();
}
})(jQuery);
if ($('#monitorWidth1').hasScrollBar()) {
// has scrollbar
$("#mtc").addClass("AdjustOverflowWidth");
alert('scrollbar present - Internet Exploder');
} else {
$("#mtc").removeClass("AdjustOverflowWidth");
}​
Wrap in a document ready
monitorWidth1 : the div where the overflow is set to auto
mtc : a container div inside monitorWidth1
AdjustOverflowWidth : a css class applied to the #mtc div when the Scrollbar is active
*Use the alert to test cross browser, and then comment out for final production code.
HTH
I'm going to extend on this even further for those poor souls who, like me, use one of the modern js frameworks and not JQuery and have been wholly abandoned by the people of this thread :
this was written in Angular 6 but if you write React 16, Vue 2, Polymer, Ionic, React-Native, you'll know what to do to adapt it. And it's the whole component so it should be easy.
import {ElementRef, AfterViewInit} from '#angular/core';
#Component({
selector: 'app',
templateUrl: './app.html',
styleUrls: ['./app.scss']
})
export class App implements AfterViewInit {
scrollAmount;
constructor(
private fb: FormBuilder,
private element: ElementRef
) {}
ngAfterViewInit(){
this.scrollAmount = this.element.nativeElement.querySelector('.elem-list');
this.scrollAmount.addEventListener('wheel', e => { //you can put () instead of e
// but e is usefull if you require the deltaY amount.
if(this.scrollAmount.scrollHeight > this.scrollAmount.offsetHeight){
// there is a scroll bar, do something!
}else{
// there is NO scroll bar, do something!
}
});
}
}
in the html there would be a div with class "elem-list" which is stylized in the css or scss to have a height and an overflow value that isn't hidden. (so auto or sroll )
I trigger this eval upon a scroll event because my end goal was to have "automatic focus scrolls" which decide whether they are scrolling the whole set of components horizontally if said components have no vertical scroll available and otherwise only scroll the innards of one of the components vertically.
but you can place the eval elsewhere to have it be triggered by something else.
the important thing to remember here, is you're never Forced back into using JQuery, there's always a way to access the same functionalities it has without using it.
The solutions provided above will work in the most cases, but checking the scrollHeight and overflow is sometimes not enough and can fail for body and html elements as seen here:
https://codepen.io/anon/pen/EvzXZw
1. Solution - Check if the element is scrollable:
function isScrollableY (element) {
return !!(element.scrollTop || (++element.scrollTop && element.scrollTop--));
}
Note: elements with overflow: hidden are also treated as scrollable (more info), so you might add a condition against that too if needed:
function isScrollableY (element) {
let style = window.getComputedStyle(element);
return !!(element.scrollTop || (++element.scrollTop && element.scrollTop--))
&& style["overflow"] !== "hidden" && style["overflow-y"] !== "hidden";
}
As far as I know this method only fails if the element has scroll-behavior: smooth.
Explanation: The trick is, that the attempt of scrolling down and reverting it won't be rendered by the browser. The topmost function can also be written like the following:
function isScrollableY (element) {
// if scrollTop is not 0 / larger than 0, then the element is scrolled and therefore must be scrollable
// -> true
if (element.scrollTop === 0) {
// if the element is zero it may be scrollable
// -> try scrolling about 1 pixel
element.scrollTop++;
// if the element is zero then scrolling did not succeed and therefore it is not scrollable
// -> false
if (element.scrollTop === 0) return false;
// else the element is scrollable; reset the scrollTop property
// -> true
element.scrollTop--;
}
return true;
}
2. Solution - Do all the necessary checks:
function isScrollableY (element) {
const style = window.getComputedStyle(element);
if (element.scrollHeight > element.clientHeight &&
style["overflow"] !== "hidden" && style["overflow-y"] !== "hidden" &&
style["overflow"] !== "clip" && style["overflow-y"] !== "clip"
) {
if (element === document.scrollingElement) return true;
else if (style["overflow"] !== "visible" && style["overflow-y"] !== "visible") {
// special check for body element (https://drafts.csswg.org/cssom-view/#potentially-scrollable)
if (element === document.body) {
const parentStyle = window.getComputedStyle(element.parentElement);
if (parentStyle["overflow"] !== "visible" && parentStyle["overflow-y"] !== "visible" &&
parentStyle["overflow"] !== "clip" && parentStyle["overflow-y"] !== "clip"
) {
return true;
}
}
else return true;
}
}
return false;
}
Here's an improved version of Evan's answer which seems to properly account for overflow logic.
function element_scrollbars(node) {
var element = $(node);
var overflow_x = element.css("overflow-x");
var overflow_y = element.css("overflow-y");
var overflow = element.css("overflow");
if (overflow_x == "undefined") overflow_x == "";
if (overflow_y == "undefined") overflow_y == "";
if (overflow == "undefined") overflow == "";
if (overflow_x == "") overflow_x = overflow;
if (overflow_y == "") overflow_y = overflow;
var scrollbar_vertical = (
(overflow_y == "scroll")
|| (
(
(overflow_y == "hidden")
|| (overflow_y == "visible")
)
&& (
(node.scrollHeight > node.clientHeight)
)
)
);
var scrollbar_horizontal = (
(overflow_x == "scroll")
|| (
(
(overflow_x == "hidden")
|| (overflow_x == "visible")
)
&& (
(node.scrollWidth > node.clientWidth)
)
)
);
return {
vertical: scrollbar_vertical,
horizontal: scrollbar_horizontal
};
}
Most of the answers presented got me close to where I needed to be, but not quite there.
We basically wanted to assess if the scroll bars -would- be visible in a normal situation, by that definition meaning that the size of the body element is larger than the view port. This was not a presented solution, which is why I am submitting it.
Hopefully it helps someone!
(function($) {
$.fn.hasScrollBar = function() {
return this.get(0).scrollHeight > $(window).height();
}
})(jQuery);
Essentially, we have the hasScrollbar function, but returning if the requested element is larger than the view port. For view port size, we just used $(window).height(). A quick compare of that against the element size, yields the correct results and desirable behavior.
Here's my improvement: added parseInt. for some weird reason it didn't work without it.
// usage: jQuery('#my_div1').hasVerticalScrollBar();
// Credit: http://stackoverflow.com/questions/4814398/how-can-i-check-if-a-scrollbar-is-visible
(function($) {
$.fn.hasVerticalScrollBar = function() {
return this.get(0) ? parseInt( this.get(0).scrollHeight ) > parseInt( this.innerHeight() ) : false;
};
})(jQuery);
Works on Chrome, Edge, Firefox and Opera, at least in the newer versions.
Using JQuery...
Setup this function to fix the footer:
function fixFooterCaller()
{
const body = $('body');
const footer = $('body footer');
return function ()
{
// If the scroll bar is visible
if ($(document).height() > $(window).height())
{
// Reset
footer.css('position', 'inherit');
// Erase the padding added in the above code
body.css('padding-bottom', '0');
}
// If the scrollbar is NOT visible
else
{
// Make it fixed at the bottom
footer.css('position', 'fixed');
// And put a padding to the body as the size of the footer
// This makes the footer do not cover the content and when
// it does, this event fix it
body.css('padding-bottom', footer.outerHeight());
}
}
}
It returns a function. Made this way just to set the body and footer once.
And then, set this when the document is ready.
$(document).ready(function ()
{
const fixFooter = fixFooterCaller();
// Put in a timeout call instead of just call the fixFooter function
// to prevent the page elements needed don't be ready at this time
setTimeout(fixFooter, 0);
// The function must be called every time the window is resized
$(window).resize(fixFooter);
});
Add this to your footer css:
footer {
bottom: 0;
}
Find a parent of current element that has vertical scrolling or body.
$.fn.scrollableParent = function() {
var $parents = this.parents();
var $scrollable = $parents.filter(function(idx) {
return this.scrollHeight > this.offsetHeight && this.offsetWidth !== this.clientWidth;
}).first();
if ($scrollable.length === 0) {
$scrollable = $('html, body');
}
return $scrollable;
};
It may be used to autoscroll to current element via:
var $scrollable = $elem.scrollableParent();
$scrollable.scrollTop($elem.position().top);
A No Framework JavaScript Approach, checks for both vertical and horizontal
/*
* hasScrollBars
*
* Checks to see if an element has scrollbars
*
* #returns {object}
*/
Element.prototype.hasScrollBars = function() {
return {"vertical": this.scrollHeight > this.style.height, "horizontal": this.scrollWidth > this.style.width};
}
Use it like this
if(document.getElementsByTagName("body")[0].hasScrollBars().vertical){
alert("vertical");
}
if(document.getElementsByTagName("body")[0].hasScrollBars().horizontal){
alert("horizontal");
}
There's two area sizes to consider, the window and the html. If the html width, for example, is greater than window width then a scroll bar may be present on the user interface. So it's a matter of reading the window proportions and the html proportions and doing the basic math.
As for displaying an arrow overlaid atop the page, that's done with a simple classlist toggle, e.g. .hidden{display:none}
Here's a crossbrowser method for getting these proportions. (credit W3 Schools)
|| document.body.clientWidth;
var h = window.innerHeight || document.documentElement.clientHeight ||
document.body.clientHeight; ```
I had the problem, that I needed to check, if the scrollbar is visible on the whole screen (body) or not. Chrome has the ability to hide the scrollbar despite the fact that there is an overflow happening, hence the body is scrollable.
Therefore the solutions above did not work for me. I now check, if there is a scrollbar the following way:
const isScrollbarPresent = () => {
const beforeScrollbarHidden = document.body.clientWidth;
const overflowState = document.body?.style.overflow;
document.body.style.overflow = 'hidden';
const afterScrollbarHidden = document.body.clientWidth;
document.body.style.overflow = overflowState;
return beforeScrollbarHidden !== afterScrollbarHidden;
};
I get the width of the body, with or without the scrollbar and save the current overflow state of the body. Then I hide the scrollbar. If there was a scrollbar, the width of the body is now bigger. If not, the width is the same. After that I revert the overflow state.
Another easy answer is the following:
export const isScrollbarPresent = (element?: HTMLElement) => {
const testedElement = element ?? document.body;
return testedElement.scrollHeight > testedElement.clientHeight; }
I check if the scrollHeight of the element is bigger then the clientHeight. The scrollHeight returns the absolute height of an element including all elements, that are not visible. The clientHeight returns the visible height on the screen.

Categories