Related
I've the following sample html, there is a DIV which has 100% width. It contains some elements. While performing windows re-sizing, the inner elements may be re-positioned, and the dimension of the div may change. I'm asking if it is possible to hook the div's dimension change event? and How to do that? I currently bind the callback function to the jQuery resize event on the target DIV, however, no console log is outputted, see below:
<html>
<head>
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" language="javascript">
$('#test_div').bind('resize', function(){
console.log('resized');
});
</script>
</head>
<body>
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" />
<input type="button" value="button 2" />
<input type="button" value="button 3" />
</div>
</body>
</html>
A newer standard for this is the Resize Observer api, with good browser support.
function outputsize() {
width.value = textbox.offsetWidth
height.value = textbox.offsetHeight
}
outputsize()
new ResizeObserver(outputsize).observe(textbox)
Width: <output id="width">0</output><br>
Height: <output id="height">0</output><br>
<textarea id="textbox">Resize me</textarea><br>
Resize Observer
Documentation: https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API
Spec: https://wicg.github.io/ResizeObserver
Current Support: http://caniuse.com/#feat=resizeobserver
Polyfills: https://github.com/pelotoncycle/resize-observer
https://github.com/que-etc/resize-observer-polyfill
https://github.com/juggle/resize-observer
There is a very efficient method to determine if a element's size has been changed.
http://marcj.github.io/css-element-queries/
This library has a class ResizeSensor which can be used for resize detection. It uses an event-based approach, so it's damn fast and doesn't waste CPU time.
Example:
new ResizeSensor(jQuery('#divId'), function(){
console.log('content dimension changed');
});
Please do not use the jQuery onresize plugin as it uses setTimeout() in combination with reading the DOM clientHeight/clientWidth properties in a loop to check for changes. This is incredible slow and inaccurate since it causes layout thrashing.
Disclosure: I am directly associated with this library.
Long term, you will be able to use the ResizeObserver.
new ResizeObserver(callback).observe(element);
Unfortunately it is not currently supported by default in many browsers.
In the mean time, you can use function like the following. Since, the majority of element size changes will come from the window resizing or from changing something in the DOM. You can listen to window resizing with the window's resize event and you can listen to DOM changes using MutationObserver.
Here's an example of a function that will call you back when the size of the provided element changes as a result of either of those events:
var onResize = function(element, callback) {
if (!onResize.watchedElementData) {
// First time we are called, create a list of watched elements
// and hook up the event listeners.
onResize.watchedElementData = [];
var checkForChanges = function() {
onResize.watchedElementData.forEach(function(data) {
if (data.element.offsetWidth !== data.offsetWidth ||
data.element.offsetHeight !== data.offsetHeight) {
data.offsetWidth = data.element.offsetWidth;
data.offsetHeight = data.element.offsetHeight;
data.callback();
}
});
};
// Listen to the window's size changes
window.addEventListener('resize', checkForChanges);
// Listen to changes on the elements in the page that affect layout
var observer = new MutationObserver(checkForChanges);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
// Save the element we are watching
onResize.watchedElementData.push({
element: element,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight,
callback: callback
});
};
I DO NOT recommend setTimeout() hack as it slows down the performance!
Instead, you can use DOM ResizeObserver method for listening to Div size change.
const myObserver = new ResizeObserver(entries => {
// this will get called whenever div dimension changes
entries.forEach(entry => {
console.log('width', entry.contentRect.width);
console.log('height', entry.contentRect.height);
});
});
const someEl = document.querySelector('.some-element');
// start listening to changes
myObserver.observe(someEl);
// later, stop listening to changes
myObserver.disconnect();
Old answer using MutationObserver:
For listening to HTML element attributes, subtree, and class changes:
JS:
var observer = new MutationObserver(function(mutations) {
console.log('size changed!');
});
var target = document.querySelector('.mydiv');
observer.observe(target, {
attributes: true,
childList: true,
subtree: true
});
HTML:
<div class='mydiv'>
</div>
Here's the fiddle.. Try to change the div size.
You can further wrap your method in the debounce method to improve efficiency. debounce will trigger your method every x milliseconds instead of triggering every millisecond the DIV is being resized.
ResizeSensor.js is part of a huge library, but I reduced its functionality to THIS:
function ResizeSensor(element, callback)
{
let zIndex = parseInt(getComputedStyle(element));
if(isNaN(zIndex)) { zIndex = 0; };
zIndex--;
let expand = document.createElement('div');
expand.style.position = "absolute";
expand.style.left = "0px";
expand.style.top = "0px";
expand.style.right = "0px";
expand.style.bottom = "0px";
expand.style.overflow = "hidden";
expand.style.zIndex = zIndex;
expand.style.visibility = "hidden";
let expandChild = document.createElement('div');
expandChild.style.position = "absolute";
expandChild.style.left = "0px";
expandChild.style.top = "0px";
expandChild.style.width = "10000000px";
expandChild.style.height = "10000000px";
expand.appendChild(expandChild);
let shrink = document.createElement('div');
shrink.style.position = "absolute";
shrink.style.left = "0px";
shrink.style.top = "0px";
shrink.style.right = "0px";
shrink.style.bottom = "0px";
shrink.style.overflow = "hidden";
shrink.style.zIndex = zIndex;
shrink.style.visibility = "hidden";
let shrinkChild = document.createElement('div');
shrinkChild.style.position = "absolute";
shrinkChild.style.left = "0px";
shrinkChild.style.top = "0px";
shrinkChild.style.width = "200%";
shrinkChild.style.height = "200%";
shrink.appendChild(shrinkChild);
element.appendChild(expand);
element.appendChild(shrink);
function setScroll()
{
expand.scrollLeft = 10000000;
expand.scrollTop = 10000000;
shrink.scrollLeft = 10000000;
shrink.scrollTop = 10000000;
};
setScroll();
let size = element.getBoundingClientRect();
let currentWidth = size.width;
let currentHeight = size.height;
let onScroll = function()
{
let size = element.getBoundingClientRect();
let newWidth = size.width;
let newHeight = size.height;
if(newWidth != currentWidth || newHeight != currentHeight)
{
currentWidth = newWidth;
currentHeight = newHeight;
callback();
}
setScroll();
};
expand.addEventListener('scroll', onScroll);
shrink.addEventListener('scroll', onScroll);
};
How to use it:
let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
You have to bind the resize event on the window object, not on a generic html element.
You could then use this:
$(window).resize(function() {
...
});
and within the callback function you can check the new width of your div calling
$('.a-selector').width();
So, the answer to your question is no, you can't bind the resize event to a div.
The best solution would be to use the so-called Element Queries. However, they are not standard, no specification exists - and the only option is to use one of the polyfills/libraries available, if you want to go this way.
The idea behind element queries is to allow a certain container on the page to respond to the space that's provided to it. This will allow to write a component once and then drop it anywhere on the page, while it will adjust its contents to its current size. No matter what the Window size is. This is the first difference that we see between element queries and media queries. Everyone hopes that at some point a specification will be created that will standardize element queries (or something that achieves the same goal) and make them native, clean, simple and robust. Most people agree that Media queries are quite limited and don't help for modular design and true responsiveness.
There are a few polyfills/libraries that solve the problem in different ways (could be called workarounds instead of solutions though):
CSS Element Queries - https://github.com/marcj/css-element-queries
BoomQueries - https://github.com/BoomTownROI/boomqueries
eq.js - https://github.com/Snugug/eq.js
ElementQuery - https://github.com/tysonmatanich/elementQuery
And a few more, which I'm not going to list here, but you're free to search. I would not be able to say which of the currently available options is the best. You'll have to try a few and decide.
I have seen other solutions to similar problems proposed. Usually they use timers or the Window/viewport size under the hood, which is not a real solution. Furthermore, I think ideally this should be solved mainly in CSS, and not in javascript or html.
I found this library to work when MarcJ's solution didn't:
https://github.com/sdecima/javascript-detect-element-resize
It's very lightweight and detects even natural resizes via CSS or simply the HTML loading/rendering.
Code sample (taken from the link):
<script type="text/javascript" src="detect-element-resize.js"></script>
<script type="text/javascript">
var resizeElement = document.getElementById('resizeElement'),
resizeCallback = function() {
/* do something */
};
addResizeListener(resizeElement, resizeCallback);
removeResizeListener(resizeElement, resizeCallback);
</script>
Take a look at this http://benalman.com/code/projects/jquery-resize/examples/resize/
It has various examples. Try resizing your window and see how elements inside container elements adjusted.
Example with js fiddle to explain how to get it work.
Take a look at this fiddle http://jsfiddle.net/sgsqJ/4/
In that resize() event is bound to an elements having class "test" and also to the window object
and in resize callback of window object $('.test').resize() is called.
e.g.
$('#test_div').bind('resize', function(){
console.log('resized');
});
$(window).resize(function(){
$('#test_div').resize();
});
Only the window object generates a "resize" event. The only way I know of to do what you want to do is to run an interval timer that periodically checks the size.
You can use iframe or object using contentWindow or contentDocument on resize. Without setInterval or setTimeout
The steps:
Set your element position to relative
Add inside an transparent absolute hidden IFRAME
Listen to IFRAME.contentWindow - onresize event
An example of HTML:
<div style="height:50px;background-color:red;position:relative;border:1px solid red">
<iframe style=width:100%;height:100%;position:absolute;border:none;background-color:transparent allowtransparency=true>
</iframe>
This is my div
</div>
The Javascript:
$('div').width(100).height(100);
$('div').animate({width:200},2000);
$('object').attr({
type : 'text/html'
})
$('object').on('resize,onresize,load,onload',function(){
console.log('ooooooooonload')
})
$($('iframe')[0].contentWindow).on('resize',function(){
console.log('div changed')
})
Running Example
JsFiddle: https://jsfiddle.net/qq8p470d/
See more:
Clay - It's based on element-resize-event
element-resize-event
var div = document.getElementById('div');
div.addEventListener('resize', (event) => console.log(event.detail));
function checkResize (mutations) {
var el = mutations[0].target;
var w = el.clientWidth;
var h = el.clientHeight;
var isChange = mutations
.map((m) => m.oldValue + '')
.some((prev) => prev.indexOf('width: ' + w + 'px') == -1 || prev.indexOf('height: ' + h + 'px') == -1);
if (!isChange)
return;
var event = new CustomEvent('resize', {detail: {width: w, height: h}});
el.dispatchEvent(event);
}
var observer = new MutationObserver(checkResize);
observer.observe(div, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
#div {width: 100px; border: 1px solid #bbb; resize: both; overflow: hidden;}
<div id = "div">DIV</div>
Amazingly as old as this issue is, this is still a problem in most browsers.
As others have said, Chrome 64+ now ships with Resize Observes natively, however, the spec is still being fine tuned and Chrome is now currently (as of 2019-01-29) behind the latest edition of the specification.
I've seen a couple of good ResizeObserver polyfills out in the wild, however, some do not follow the specification that closely and others have some calculation issues.
I was in desperate need of this behaviour to create some responsive web components that could be used in any application. To make them work nicely they need to know their dimensions at all times, so ResizeObservers sounded ideal and I decided to create a polyfill that followed the spec as closely as possible.
Repo:
https://github.com/juggle/resize-observer
Demo:
https://codesandbox.io/s/myqzvpmmy9
Using Clay.js (https://github.com/zzarcon/clay) it's quite simple to detect changes on element size:
var el = new Clay('.element');
el.on('resize', function(size) {
console.log(size.height, size.width);
});
Here is a simplified version of the solution by #nkron, applicable to a single element (instead of an array of elements in #nkron's answer, complexity I did not need).
function onResizeElem(element, callback) {
// Save the element we are watching
onResizeElem.watchedElementData = {
element: element,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight,
callback: callback
};
onResizeElem.checkForChanges = function() {
const data = onResizeElem.watchedElementData;
if (data.element.offsetWidth !== data.offsetWidth || data.element.offsetHeight !== data.offsetHeight) {
data.offsetWidth = data.element.offsetWidth;
data.offsetHeight = data.element.offsetHeight;
data.callback();
}
};
// Listen to the window resize event
window.addEventListener('resize', onResizeElem.checkForChanges);
// Listen to the element being checked for width and height changes
onResizeElem.observer = new MutationObserver(onResizeElem.checkForChanges);
onResizeElem.observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
The event listener and observer can be removed by:
window.removeEventListener('resize', onResizeElem.checkForChanges);
onResizeElem.observer.disconnect();
This blog post helped me efficiently detect size changes to DOM elements.
http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/
How to use this code...
AppConfig.addResizeListener(document.getElementById('id'), function () {
//Your code to execute on resize.
});
Packaged code used by the example...
var AppConfig = AppConfig || {};
AppConfig.ResizeListener = (function () {
var attachEvent = document.attachEvent;
var isIE = navigator.userAgent.match(/Trident/);
var requestFrame = (function () {
var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
function (fn) { return window.setTimeout(fn, 20); };
return function (fn) { return raf(fn); };
})();
var cancelFrame = (function () {
var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
window.clearTimeout;
return function (id) { return cancel(id); };
})();
function resizeListener(e) {
var win = e.target || e.srcElement;
if (win.__resizeRAF__) cancelFrame(win.__resizeRAF__);
win.__resizeRAF__ = requestFrame(function () {
var trigger = win.__resizeTrigger__;
trigger.__resizeListeners__.forEach(function (fn) {
fn.call(trigger, e);
});
});
}
function objectLoad(e) {
this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
this.contentDocument.defaultView.addEventListener('resize', resizeListener);
}
AppConfig.addResizeListener = function (element, fn) {
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
if (attachEvent) {
element.__resizeTrigger__ = element;
element.attachEvent('onresize', resizeListener);
} else {
if (getComputedStyle(element).position === 'static') element.style.position = 'relative';
var obj = element.__resizeTrigger__ = document.createElement('object');
obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
obj.__resizeElement__ = element;
obj.onload = objectLoad;
obj.type = 'text/html';
if (isIE) element.appendChild(obj);
obj.data = 'about:blank';
if (!isIE) element.appendChild(obj);
}
}
element.__resizeListeners__.push(fn);
};
AppConfig.removeResizeListener = function (element, fn) {
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
if (!element.__resizeListeners__.length) {
if (attachEvent) element.detachEvent('onresize', resizeListener);
else {
element.__resizeTrigger__.contentDocument.defaultView.removeEventListener('resize', resizeListener);
element.__resizeTrigger__ = !element.removeChild(element.__resizeTrigger__);
}
}
}
})();
Note: AppConfig is a namespace/object I use for organizing reusable functions. Feel free to search and replace the name with anything you would like.
My jQuery plugin enables the "resize" event on all elements not just the window.
https://github.com/dustinpoissant/ResizeTriggering
$("#myElement") .resizeTriggering().on("resize", function(e){
// Code to handle resize
});
You can try the code in the following snippet, it covers your needs using plain javascript. (run the code snippet and click full page link to trigger the alert that the div is resized if you want to test it.).
Based on the fact that this is a setInterval of 100 milliseconds, i would dare to say that my PC did not find it too much CPU hungry. (0.1% of CPU was used as total for all opened tabs in Chrome at the time tested.). But then again this is for just one div, if you would like to do this for a large amount of elements then yes it could be very CPU hungry.
You could always use a click event to stop the div-resize sniffing anyway.
var width = 0;
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
}
if(document.getElementById("test_div").clientWidth!==width) {
alert('resized div');
width = document.getElementById("test_div").clientWidth;
}
}, 100);
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" />
<input type="button" value="button 2" />
<input type="button" value="button 3" />
</div>
You can check the fiddle also
UPDATE
var width = 0;
function myInterval() {
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
}
if(document.getElementById("test_div").clientWidth!==width) {
alert('resized');
width = document.getElementById("test_div").clientWidth;
}
}, 100);
return interval;
}
var interval = myInterval();
document.getElementById("clickMe").addEventListener( "click" , function() {
if(typeof interval!=="undefined") {
clearInterval(interval);
alert("stopped div-resize sniffing");
}
});
document.getElementById("clickMeToo").addEventListener( "click" , function() {
myInterval();
alert("started div-resize sniffing");
});
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" id="clickMe" />
<input type="button" value="button 2" id="clickMeToo" />
<input type="button" value="button 3" />
</div>
Updated Fiddle
This is pretty much an exact copy of the top answer, but instead of a link, it's just the part of the code that matters, translated to be IMO more readable and easier to understand. A few other small changes include using cloneNode(), and not putting html into a js string. Small stuff, but you can copy and paste this as is and it will work.
The way it works is by making two invisible divs fill the element you're watching, and then putting a trigger in each, and setting a scroll position that will lead to triggering a scroll change if the size changes.
All real credit goes to Marc J, but if you're just looking for the relevant code, here it is:
window.El = {}
El.resizeSensorNode = undefined;
El.initResizeNode = function() {
var fillParent = "display: block; position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;";
var triggerStyle = "position: absolute; left: 0; top: 0; transition: 0s;";
var resizeSensor = El.resizeSensorNode = document.createElement("resizeSensor");
resizeSensor.style = fillParent;
var expandSensor = document.createElement("div");
expandSensor.style = fillParent;
resizeSensor.appendChild(expandSensor);
var trigger = document.createElement("div");
trigger.style = triggerStyle;
expandSensor.appendChild(trigger);
var shrinkSensor = expandSensor.cloneNode(true);
shrinkSensor.firstChild.style = triggerStyle + " width: 200%; height: 200%";
resizeSensor.appendChild(shrinkSensor);
}
El.onSizeChange = function(domNode, fn) {
if (!domNode) return;
if (domNode.resizeListeners) {
domNode.resizeListeners.push(fn);
return;
}
domNode.resizeListeners = [];
domNode.resizeListeners.push(fn);
if(El.resizeSensorNode == undefined)
El.initResizeNode();
domNode.resizeSensor = El.resizeSensorNode.cloneNode(true);
domNode.appendChild(domNode.resizeSensor);
var expand = domNode.resizeSensor.firstChild;
var expandTrigger = expand.firstChild;
var shrink = domNode.resizeSensor.childNodes[1];
var reset = function() {
expandTrigger.style.width = '100000px';
expandTrigger.style.height = '100000px';
expand.scrollLeft = 100000;
expand.scrollTop = 100000;
shrink.scrollLeft = 100000;
shrink.scrollTop = 100000;
};
reset();
var hasChanged, frameRequest, newWidth, newHeight;
var lastWidth = domNode.offsetWidth;
var lastHeight = domNode.offsetHeight;
var onResized = function() {
frameRequest = undefined;
if (!hasChanged) return;
lastWidth = newWidth;
lastHeight = newHeight;
var listeners = domNode.resizeListeners;
for(var i = 0; listeners && i < listeners.length; i++)
listeners[i]();
};
var onScroll = function() {
newWidth = domNode.offsetWidth;
newHeight = domNode.offsetHeight;
hasChanged = newWidth != lastWidth || newHeight != lastHeight;
if (hasChanged && !frameRequest) {
frameRequest = requestAnimationFrame(onResized);
}
reset();
};
expand.addEventListener("scroll", onScroll);
shrink.addEventListener("scroll", onScroll);
}
Pure Javascript solution, but works only if the element is resized with the css resize button:
store element size with offsetWidth and offsetHeight;
add an onclick event listener on this element;
when triggered, compare curent offsetWidth and offsetHeight with stored values, and if different, do what you want and update these values.
jQuery(document).ready( function($) {
function resizeMapDIVs() {
// check the parent value...
var size = $('#map').parent().width();
if( $size < 640 ) {
// ...and decrease...
} else {
// ..or increase as necessary
}
}
resizeMapDIVs();
$(window).resize(resizeMapDIVs);
});
using Bharat Patil answer simply return false inside the your bind callback to prevent maximum stack error see example below:
$('#test_div').bind('resize', function(){
console.log('resized');
return false;
});
This is a really old question, but I figured I'd post my solution to this.
I tried to use ResizeSensor since everyone seemed to have a pretty big crush on it. After implementing though, I realized that under the hood the Element Query requires the element in question to have position relative or absolute applied to it, which didn't work for my situation.
I ended up handling this with an Rxjs interval instead of a straight setTimeout or requestAnimationFrame like previous implementations.
What's nice about the observable flavor of an interval is that you get to modify the stream however any other observable can be handled. For me, a basic implementation was enough, but you could go crazy and do all sorts of merges, etc.
In the below example, I'm tracking the inner (green) div's width changes. It has a width set to 50%, but a max-width of 200px. Dragging the slider affects the wrapper (gray) div's width. You can see that the observable only fires when the inner div's width changes, which only happens if the outer div's width is smaller than 400px.
const { interval } = rxjs;
const { distinctUntilChanged, map, filter } = rxjs.operators;
const wrapper = document.getElementById('my-wrapper');
const input = document.getElementById('width-input');
function subscribeToResize() {
const timer = interval(100);
const myDiv = document.getElementById('my-div');
const widthElement = document.getElementById('width');
const isMax = document.getElementById('is-max');
/*
NOTE: This is the important bit here
*/
timer
.pipe(
map(() => myDiv ? Math.round(myDiv.getBoundingClientRect().width) : 0),
distinctUntilChanged(),
// adding a takeUntil(), here as well would allow cleanup when the component is destroyed
)
.subscribe((width) => {
widthElement.innerHTML = width;
isMax.innerHTML = width === 200 ? 'Max width' : '50% width';
});
}
function defineRange() {
input.min = 200;
input.max = window.innerWidth;
input.step = 10;
input.value = input.max - 50;
}
function bindInputToWrapper() {
input.addEventListener('input', (event) => {
wrapper.style.width = `${event.target.value}px`;
});
}
defineRange();
subscribeToResize();
bindInputToWrapper();
.inner {
width: 50%;
max-width: 200px;
}
/* Aesthetic styles only */
.inner {
background: #16a085;
}
.wrapper {
background: #ecf0f1;
color: white;
margin-top: 24px;
}
.content {
padding: 12px;
}
body {
font-family: sans-serif;
font-weight: bold;
}
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
<h1>Resize Browser width</h1>
<label for="width-input">Adjust the width of the wrapper element</label>
<div>
<input type="range" id="width-input">
</div>
<div id="my-wrapper" class="wrapper">
<div id="my-div" class="inner">
<div class="content">
Width: <span id="width"></span>px
<div id="is-max"></div>
</div>
</div>
</div>
expanding on this answer by #gman, here's a function that allows multiple per element callbacks, exploding the width and height into a quasi event object. see embedded demo that works live here on stack overflow ( you may need to resize the main browser drastically for it to trigger)
function elementResizeWatcher(element, callback) {
var
resolve=function(element) {
return (typeof element==='string'
? document[
['.','#'].indexOf(element.charAt(0)) < 0 ? "getElementById" : "querySelector"
] (element)
: element);
},
observer,
watched = [],
checkForElementChanges = function (data) {
var w=data.el.offsetWidth,h=data.el.offsetHeight;
if (
data.offsetWidth !== w ||
data.offsetHeight !== h
) {
data.offsetWidth = w;
data.offsetHeight = h;
data.cb({
target : data.el,
width : w,
height : h
});
}
},
checkForChanges=function(){
watched.forEach(checkForElementChanges);
},
started=false,
self = {
start: function () {
if (!started) {
// Listen to the window resize event
window.addEventListener("resize", checkForChanges);
// Listen to the element being checked for width and height changes
observer = new MutationObserver(checkForChanges);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
started=true;
}
},
stop : function ( ) {
if (started) {
window.removeEventListener('resize', checkForChanges);
observer.disconnect();
started = false;
}
},
addListener : function (element,callback) {
if (typeof callback!=='function')
return;
var el = resolve(element);
if (typeof el==='object') {
watched.push({
el : el,
offsetWidth : el.offsetWidth,
offsetHeight : el.offsetHeight,
cb : callback
});
}
},
removeListener : function (element,callback) {
var
el = resolve(element);
watched = watched.filter(function(data){
return !((data.el===el) && (data.cb===callback));
});
}
};
self.addListener(element,callback);
self.start();
return self;
}
var watcher = elementResizeWatcher("#resize_me_on_stack_overflow", function(e){
e.target.innerHTML="i am "+e.width+"px x "+e.height+"px";
});
watcher.addListener(".resize_metoo",function(e) {
e.target.innerHTML="and i am "+e.width+"px x "+e.height+"px";
});
var mainsize_info = document.getElementById("mainsize");
watcher.addListener(document.body,function(e) {
mainsize_info.innerHTML=e.width+"px x "+e.height+"px";
});
#resize_me_on_stack_overflow{
background-color:lime;
}
.resize_metoo {
background-color:yellow;
font-size:36pt;
width:50%;
}
<p> resize the main browser window! <span id="mainsize"><span> </p>
<p id="resize_me_on_stack_overflow">
hey, resize me.
</p>
<p class="resize_metoo">
resize me too.
</p>
Pure vanilla implementation.
var move = function(e) {
if ((e.w && e.w !== e.offsetWidth) || (e.h && e.h !== e.offsetHeight)) {
new Function(e.getAttribute('onresize')).call(e);
}
e.w = e.offsetWidth;
e.h = e.offsetHeight;
}
var resize = function(e) {
e.innerText = 'New dimensions: ' + e.w + ',' + e.h;
}
.resizable {
resize: both;
overflow: auto;
width: 200px;
border: 1px solid black;
padding: 20px;
}
<div class='resizable' onresize="resize(this)" onmousemove="move(this)">
Pure vanilla implementation
</div>
With disconnect to remove the event listener:
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "context", "output"]
connect() {
this.inputObserver = new ResizeObserver(() => { this.resizeInput() })
this.inputObserver.observe(this.inputTarget)
}
disconnect() {
this.inputObserver.disconnect(this.inputTarget)
}
resizeInput() {
const height = this.inputTarget.offsetHeight
this.contextTarget.style.height = `${height}px`
this.outputTarget.style.height = `${height}px`
}
}
Only Window.onResize exists in the specification, but you can always utilize IFrame to generate new Window object inside your DIV.
Please check this answer. There is a new little jquery plugin, that is portable and easy to use. You can always check the source code to see how it's done.
<!-- (1) include plugin script in a page -->
<script src="/src/jquery-element-onresize.js"></script>
// (2) use the detectResizing plugin to monitor changes to the element's size:
$monitoredElement.detectResizing({ onResize: monitoredElement_onResize });
// (3) write a function to react on changes:
function monitoredElement_onResize() {
// logic here...
}
i thought it couldn't be done but then i thought about it, you can manually resize a div via style="resize: both;" in order to do that you ave to click on it so added an onclick function to check element's height and width and it worked. With only 5 lines of pure javascript (sure it could be even shorter)
http://codepen.io/anon/pen/eNyyVN
<div id="box" style="
height:200px;
width:640px;
background-color:#FF0066;
resize: both;
overflow: auto;"
onclick="myFunction()">
<p id="sizeTXT" style="
font-size: 50px;">
WxH
</p>
</div>
<p>This my example demonstrates how to run a resize check on click for resizable div.</p>
<p>Try to resize the box.</p>
<script>
function myFunction() {
var boxheight = document.getElementById('box').offsetHeight;
var boxhwidth = document.getElementById('box').offsetWidth;
var txt = boxhwidth +"x"+boxheight;
document.getElementById("sizeTXT").innerHTML = txt;
}
</script>
How would you display a div/class that has already been hidden by a button after said button has been hidden due to screen resolution css using #media. This is the code for the button:
<script>
var button = document.getElementById('answer');
function showDiv() {
var div = document.getElementById('displayfilter');
if (div.style.display !== 'none') {
div.style.display = 'none';
}
else {
div.style.display = 'block';
}
};
</script>
Here is the css for the screen res;
#media only screen and (max-width: 1026px) {
.displayfilter {
display: none;
}
.answer {
display: block !important;
}
}
The above works fine until the screen is returned to normal size. Thanks!
If I understood correctly the question, if the page is resized wider than 1026 and the div is hidden then show it:
window.onresize = function(event) {
if (window.innerWidth > 1026 && div.style.display == 'none') {
div.style.display = 'block';
}
};
If you want to display the div by default on large screens you need to take the displayfilter class and add display:block; to it but outside of the media query, like so:
.displayfilter {
display: block;
}
At the moment you are saying If [device width] is less than or equal to [specified #], then do {...}. Your specified device width being 1026px. You are not telling the stylesheet to display it by default so it is not being displayed.
I've the following sample html, there is a DIV which has 100% width. It contains some elements. While performing windows re-sizing, the inner elements may be re-positioned, and the dimension of the div may change. I'm asking if it is possible to hook the div's dimension change event? and How to do that? I currently bind the callback function to the jQuery resize event on the target DIV, however, no console log is outputted, see below:
<html>
<head>
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" language="javascript">
$('#test_div').bind('resize', function(){
console.log('resized');
});
</script>
</head>
<body>
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" />
<input type="button" value="button 2" />
<input type="button" value="button 3" />
</div>
</body>
</html>
A newer standard for this is the Resize Observer api, with good browser support.
function outputsize() {
width.value = textbox.offsetWidth
height.value = textbox.offsetHeight
}
outputsize()
new ResizeObserver(outputsize).observe(textbox)
Width: <output id="width">0</output><br>
Height: <output id="height">0</output><br>
<textarea id="textbox">Resize me</textarea><br>
Resize Observer
Documentation: https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API
Spec: https://wicg.github.io/ResizeObserver
Current Support: http://caniuse.com/#feat=resizeobserver
Polyfills: https://github.com/pelotoncycle/resize-observer
https://github.com/que-etc/resize-observer-polyfill
https://github.com/juggle/resize-observer
There is a very efficient method to determine if a element's size has been changed.
http://marcj.github.io/css-element-queries/
This library has a class ResizeSensor which can be used for resize detection. It uses an event-based approach, so it's damn fast and doesn't waste CPU time.
Example:
new ResizeSensor(jQuery('#divId'), function(){
console.log('content dimension changed');
});
Please do not use the jQuery onresize plugin as it uses setTimeout() in combination with reading the DOM clientHeight/clientWidth properties in a loop to check for changes. This is incredible slow and inaccurate since it causes layout thrashing.
Disclosure: I am directly associated with this library.
Long term, you will be able to use the ResizeObserver.
new ResizeObserver(callback).observe(element);
Unfortunately it is not currently supported by default in many browsers.
In the mean time, you can use function like the following. Since, the majority of element size changes will come from the window resizing or from changing something in the DOM. You can listen to window resizing with the window's resize event and you can listen to DOM changes using MutationObserver.
Here's an example of a function that will call you back when the size of the provided element changes as a result of either of those events:
var onResize = function(element, callback) {
if (!onResize.watchedElementData) {
// First time we are called, create a list of watched elements
// and hook up the event listeners.
onResize.watchedElementData = [];
var checkForChanges = function() {
onResize.watchedElementData.forEach(function(data) {
if (data.element.offsetWidth !== data.offsetWidth ||
data.element.offsetHeight !== data.offsetHeight) {
data.offsetWidth = data.element.offsetWidth;
data.offsetHeight = data.element.offsetHeight;
data.callback();
}
});
};
// Listen to the window's size changes
window.addEventListener('resize', checkForChanges);
// Listen to changes on the elements in the page that affect layout
var observer = new MutationObserver(checkForChanges);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
// Save the element we are watching
onResize.watchedElementData.push({
element: element,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight,
callback: callback
});
};
I DO NOT recommend setTimeout() hack as it slows down the performance!
Instead, you can use DOM ResizeObserver method for listening to Div size change.
const myObserver = new ResizeObserver(entries => {
// this will get called whenever div dimension changes
entries.forEach(entry => {
console.log('width', entry.contentRect.width);
console.log('height', entry.contentRect.height);
});
});
const someEl = document.querySelector('.some-element');
// start listening to changes
myObserver.observe(someEl);
// later, stop listening to changes
myObserver.disconnect();
Old answer using MutationObserver:
For listening to HTML element attributes, subtree, and class changes:
JS:
var observer = new MutationObserver(function(mutations) {
console.log('size changed!');
});
var target = document.querySelector('.mydiv');
observer.observe(target, {
attributes: true,
childList: true,
subtree: true
});
HTML:
<div class='mydiv'>
</div>
Here's the fiddle.. Try to change the div size.
You can further wrap your method in the debounce method to improve efficiency. debounce will trigger your method every x milliseconds instead of triggering every millisecond the DIV is being resized.
ResizeSensor.js is part of a huge library, but I reduced its functionality to THIS:
function ResizeSensor(element, callback)
{
let zIndex = parseInt(getComputedStyle(element));
if(isNaN(zIndex)) { zIndex = 0; };
zIndex--;
let expand = document.createElement('div');
expand.style.position = "absolute";
expand.style.left = "0px";
expand.style.top = "0px";
expand.style.right = "0px";
expand.style.bottom = "0px";
expand.style.overflow = "hidden";
expand.style.zIndex = zIndex;
expand.style.visibility = "hidden";
let expandChild = document.createElement('div');
expandChild.style.position = "absolute";
expandChild.style.left = "0px";
expandChild.style.top = "0px";
expandChild.style.width = "10000000px";
expandChild.style.height = "10000000px";
expand.appendChild(expandChild);
let shrink = document.createElement('div');
shrink.style.position = "absolute";
shrink.style.left = "0px";
shrink.style.top = "0px";
shrink.style.right = "0px";
shrink.style.bottom = "0px";
shrink.style.overflow = "hidden";
shrink.style.zIndex = zIndex;
shrink.style.visibility = "hidden";
let shrinkChild = document.createElement('div');
shrinkChild.style.position = "absolute";
shrinkChild.style.left = "0px";
shrinkChild.style.top = "0px";
shrinkChild.style.width = "200%";
shrinkChild.style.height = "200%";
shrink.appendChild(shrinkChild);
element.appendChild(expand);
element.appendChild(shrink);
function setScroll()
{
expand.scrollLeft = 10000000;
expand.scrollTop = 10000000;
shrink.scrollLeft = 10000000;
shrink.scrollTop = 10000000;
};
setScroll();
let size = element.getBoundingClientRect();
let currentWidth = size.width;
let currentHeight = size.height;
let onScroll = function()
{
let size = element.getBoundingClientRect();
let newWidth = size.width;
let newHeight = size.height;
if(newWidth != currentWidth || newHeight != currentHeight)
{
currentWidth = newWidth;
currentHeight = newHeight;
callback();
}
setScroll();
};
expand.addEventListener('scroll', onScroll);
shrink.addEventListener('scroll', onScroll);
};
How to use it:
let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
You have to bind the resize event on the window object, not on a generic html element.
You could then use this:
$(window).resize(function() {
...
});
and within the callback function you can check the new width of your div calling
$('.a-selector').width();
So, the answer to your question is no, you can't bind the resize event to a div.
The best solution would be to use the so-called Element Queries. However, they are not standard, no specification exists - and the only option is to use one of the polyfills/libraries available, if you want to go this way.
The idea behind element queries is to allow a certain container on the page to respond to the space that's provided to it. This will allow to write a component once and then drop it anywhere on the page, while it will adjust its contents to its current size. No matter what the Window size is. This is the first difference that we see between element queries and media queries. Everyone hopes that at some point a specification will be created that will standardize element queries (or something that achieves the same goal) and make them native, clean, simple and robust. Most people agree that Media queries are quite limited and don't help for modular design and true responsiveness.
There are a few polyfills/libraries that solve the problem in different ways (could be called workarounds instead of solutions though):
CSS Element Queries - https://github.com/marcj/css-element-queries
BoomQueries - https://github.com/BoomTownROI/boomqueries
eq.js - https://github.com/Snugug/eq.js
ElementQuery - https://github.com/tysonmatanich/elementQuery
And a few more, which I'm not going to list here, but you're free to search. I would not be able to say which of the currently available options is the best. You'll have to try a few and decide.
I have seen other solutions to similar problems proposed. Usually they use timers or the Window/viewport size under the hood, which is not a real solution. Furthermore, I think ideally this should be solved mainly in CSS, and not in javascript or html.
I found this library to work when MarcJ's solution didn't:
https://github.com/sdecima/javascript-detect-element-resize
It's very lightweight and detects even natural resizes via CSS or simply the HTML loading/rendering.
Code sample (taken from the link):
<script type="text/javascript" src="detect-element-resize.js"></script>
<script type="text/javascript">
var resizeElement = document.getElementById('resizeElement'),
resizeCallback = function() {
/* do something */
};
addResizeListener(resizeElement, resizeCallback);
removeResizeListener(resizeElement, resizeCallback);
</script>
Take a look at this http://benalman.com/code/projects/jquery-resize/examples/resize/
It has various examples. Try resizing your window and see how elements inside container elements adjusted.
Example with js fiddle to explain how to get it work.
Take a look at this fiddle http://jsfiddle.net/sgsqJ/4/
In that resize() event is bound to an elements having class "test" and also to the window object
and in resize callback of window object $('.test').resize() is called.
e.g.
$('#test_div').bind('resize', function(){
console.log('resized');
});
$(window).resize(function(){
$('#test_div').resize();
});
Only the window object generates a "resize" event. The only way I know of to do what you want to do is to run an interval timer that periodically checks the size.
You can use iframe or object using contentWindow or contentDocument on resize. Without setInterval or setTimeout
The steps:
Set your element position to relative
Add inside an transparent absolute hidden IFRAME
Listen to IFRAME.contentWindow - onresize event
An example of HTML:
<div style="height:50px;background-color:red;position:relative;border:1px solid red">
<iframe style=width:100%;height:100%;position:absolute;border:none;background-color:transparent allowtransparency=true>
</iframe>
This is my div
</div>
The Javascript:
$('div').width(100).height(100);
$('div').animate({width:200},2000);
$('object').attr({
type : 'text/html'
})
$('object').on('resize,onresize,load,onload',function(){
console.log('ooooooooonload')
})
$($('iframe')[0].contentWindow).on('resize',function(){
console.log('div changed')
})
Running Example
JsFiddle: https://jsfiddle.net/qq8p470d/
See more:
Clay - It's based on element-resize-event
element-resize-event
var div = document.getElementById('div');
div.addEventListener('resize', (event) => console.log(event.detail));
function checkResize (mutations) {
var el = mutations[0].target;
var w = el.clientWidth;
var h = el.clientHeight;
var isChange = mutations
.map((m) => m.oldValue + '')
.some((prev) => prev.indexOf('width: ' + w + 'px') == -1 || prev.indexOf('height: ' + h + 'px') == -1);
if (!isChange)
return;
var event = new CustomEvent('resize', {detail: {width: w, height: h}});
el.dispatchEvent(event);
}
var observer = new MutationObserver(checkResize);
observer.observe(div, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
#div {width: 100px; border: 1px solid #bbb; resize: both; overflow: hidden;}
<div id = "div">DIV</div>
Amazingly as old as this issue is, this is still a problem in most browsers.
As others have said, Chrome 64+ now ships with Resize Observes natively, however, the spec is still being fine tuned and Chrome is now currently (as of 2019-01-29) behind the latest edition of the specification.
I've seen a couple of good ResizeObserver polyfills out in the wild, however, some do not follow the specification that closely and others have some calculation issues.
I was in desperate need of this behaviour to create some responsive web components that could be used in any application. To make them work nicely they need to know their dimensions at all times, so ResizeObservers sounded ideal and I decided to create a polyfill that followed the spec as closely as possible.
Repo:
https://github.com/juggle/resize-observer
Demo:
https://codesandbox.io/s/myqzvpmmy9
Using Clay.js (https://github.com/zzarcon/clay) it's quite simple to detect changes on element size:
var el = new Clay('.element');
el.on('resize', function(size) {
console.log(size.height, size.width);
});
Here is a simplified version of the solution by #nkron, applicable to a single element (instead of an array of elements in #nkron's answer, complexity I did not need).
function onResizeElem(element, callback) {
// Save the element we are watching
onResizeElem.watchedElementData = {
element: element,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight,
callback: callback
};
onResizeElem.checkForChanges = function() {
const data = onResizeElem.watchedElementData;
if (data.element.offsetWidth !== data.offsetWidth || data.element.offsetHeight !== data.offsetHeight) {
data.offsetWidth = data.element.offsetWidth;
data.offsetHeight = data.element.offsetHeight;
data.callback();
}
};
// Listen to the window resize event
window.addEventListener('resize', onResizeElem.checkForChanges);
// Listen to the element being checked for width and height changes
onResizeElem.observer = new MutationObserver(onResizeElem.checkForChanges);
onResizeElem.observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
The event listener and observer can be removed by:
window.removeEventListener('resize', onResizeElem.checkForChanges);
onResizeElem.observer.disconnect();
This blog post helped me efficiently detect size changes to DOM elements.
http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/
How to use this code...
AppConfig.addResizeListener(document.getElementById('id'), function () {
//Your code to execute on resize.
});
Packaged code used by the example...
var AppConfig = AppConfig || {};
AppConfig.ResizeListener = (function () {
var attachEvent = document.attachEvent;
var isIE = navigator.userAgent.match(/Trident/);
var requestFrame = (function () {
var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
function (fn) { return window.setTimeout(fn, 20); };
return function (fn) { return raf(fn); };
})();
var cancelFrame = (function () {
var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
window.clearTimeout;
return function (id) { return cancel(id); };
})();
function resizeListener(e) {
var win = e.target || e.srcElement;
if (win.__resizeRAF__) cancelFrame(win.__resizeRAF__);
win.__resizeRAF__ = requestFrame(function () {
var trigger = win.__resizeTrigger__;
trigger.__resizeListeners__.forEach(function (fn) {
fn.call(trigger, e);
});
});
}
function objectLoad(e) {
this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
this.contentDocument.defaultView.addEventListener('resize', resizeListener);
}
AppConfig.addResizeListener = function (element, fn) {
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
if (attachEvent) {
element.__resizeTrigger__ = element;
element.attachEvent('onresize', resizeListener);
} else {
if (getComputedStyle(element).position === 'static') element.style.position = 'relative';
var obj = element.__resizeTrigger__ = document.createElement('object');
obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
obj.__resizeElement__ = element;
obj.onload = objectLoad;
obj.type = 'text/html';
if (isIE) element.appendChild(obj);
obj.data = 'about:blank';
if (!isIE) element.appendChild(obj);
}
}
element.__resizeListeners__.push(fn);
};
AppConfig.removeResizeListener = function (element, fn) {
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
if (!element.__resizeListeners__.length) {
if (attachEvent) element.detachEvent('onresize', resizeListener);
else {
element.__resizeTrigger__.contentDocument.defaultView.removeEventListener('resize', resizeListener);
element.__resizeTrigger__ = !element.removeChild(element.__resizeTrigger__);
}
}
}
})();
Note: AppConfig is a namespace/object I use for organizing reusable functions. Feel free to search and replace the name with anything you would like.
My jQuery plugin enables the "resize" event on all elements not just the window.
https://github.com/dustinpoissant/ResizeTriggering
$("#myElement") .resizeTriggering().on("resize", function(e){
// Code to handle resize
});
You can try the code in the following snippet, it covers your needs using plain javascript. (run the code snippet and click full page link to trigger the alert that the div is resized if you want to test it.).
Based on the fact that this is a setInterval of 100 milliseconds, i would dare to say that my PC did not find it too much CPU hungry. (0.1% of CPU was used as total for all opened tabs in Chrome at the time tested.). But then again this is for just one div, if you would like to do this for a large amount of elements then yes it could be very CPU hungry.
You could always use a click event to stop the div-resize sniffing anyway.
var width = 0;
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
}
if(document.getElementById("test_div").clientWidth!==width) {
alert('resized div');
width = document.getElementById("test_div").clientWidth;
}
}, 100);
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" />
<input type="button" value="button 2" />
<input type="button" value="button 3" />
</div>
You can check the fiddle also
UPDATE
var width = 0;
function myInterval() {
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
}
if(document.getElementById("test_div").clientWidth!==width) {
alert('resized');
width = document.getElementById("test_div").clientWidth;
}
}, 100);
return interval;
}
var interval = myInterval();
document.getElementById("clickMe").addEventListener( "click" , function() {
if(typeof interval!=="undefined") {
clearInterval(interval);
alert("stopped div-resize sniffing");
}
});
document.getElementById("clickMeToo").addEventListener( "click" , function() {
myInterval();
alert("started div-resize sniffing");
});
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" id="clickMe" />
<input type="button" value="button 2" id="clickMeToo" />
<input type="button" value="button 3" />
</div>
Updated Fiddle
This is pretty much an exact copy of the top answer, but instead of a link, it's just the part of the code that matters, translated to be IMO more readable and easier to understand. A few other small changes include using cloneNode(), and not putting html into a js string. Small stuff, but you can copy and paste this as is and it will work.
The way it works is by making two invisible divs fill the element you're watching, and then putting a trigger in each, and setting a scroll position that will lead to triggering a scroll change if the size changes.
All real credit goes to Marc J, but if you're just looking for the relevant code, here it is:
window.El = {}
El.resizeSensorNode = undefined;
El.initResizeNode = function() {
var fillParent = "display: block; position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;";
var triggerStyle = "position: absolute; left: 0; top: 0; transition: 0s;";
var resizeSensor = El.resizeSensorNode = document.createElement("resizeSensor");
resizeSensor.style = fillParent;
var expandSensor = document.createElement("div");
expandSensor.style = fillParent;
resizeSensor.appendChild(expandSensor);
var trigger = document.createElement("div");
trigger.style = triggerStyle;
expandSensor.appendChild(trigger);
var shrinkSensor = expandSensor.cloneNode(true);
shrinkSensor.firstChild.style = triggerStyle + " width: 200%; height: 200%";
resizeSensor.appendChild(shrinkSensor);
}
El.onSizeChange = function(domNode, fn) {
if (!domNode) return;
if (domNode.resizeListeners) {
domNode.resizeListeners.push(fn);
return;
}
domNode.resizeListeners = [];
domNode.resizeListeners.push(fn);
if(El.resizeSensorNode == undefined)
El.initResizeNode();
domNode.resizeSensor = El.resizeSensorNode.cloneNode(true);
domNode.appendChild(domNode.resizeSensor);
var expand = domNode.resizeSensor.firstChild;
var expandTrigger = expand.firstChild;
var shrink = domNode.resizeSensor.childNodes[1];
var reset = function() {
expandTrigger.style.width = '100000px';
expandTrigger.style.height = '100000px';
expand.scrollLeft = 100000;
expand.scrollTop = 100000;
shrink.scrollLeft = 100000;
shrink.scrollTop = 100000;
};
reset();
var hasChanged, frameRequest, newWidth, newHeight;
var lastWidth = domNode.offsetWidth;
var lastHeight = domNode.offsetHeight;
var onResized = function() {
frameRequest = undefined;
if (!hasChanged) return;
lastWidth = newWidth;
lastHeight = newHeight;
var listeners = domNode.resizeListeners;
for(var i = 0; listeners && i < listeners.length; i++)
listeners[i]();
};
var onScroll = function() {
newWidth = domNode.offsetWidth;
newHeight = domNode.offsetHeight;
hasChanged = newWidth != lastWidth || newHeight != lastHeight;
if (hasChanged && !frameRequest) {
frameRequest = requestAnimationFrame(onResized);
}
reset();
};
expand.addEventListener("scroll", onScroll);
shrink.addEventListener("scroll", onScroll);
}
Pure Javascript solution, but works only if the element is resized with the css resize button:
store element size with offsetWidth and offsetHeight;
add an onclick event listener on this element;
when triggered, compare curent offsetWidth and offsetHeight with stored values, and if different, do what you want and update these values.
jQuery(document).ready( function($) {
function resizeMapDIVs() {
// check the parent value...
var size = $('#map').parent().width();
if( $size < 640 ) {
// ...and decrease...
} else {
// ..or increase as necessary
}
}
resizeMapDIVs();
$(window).resize(resizeMapDIVs);
});
using Bharat Patil answer simply return false inside the your bind callback to prevent maximum stack error see example below:
$('#test_div').bind('resize', function(){
console.log('resized');
return false;
});
This is a really old question, but I figured I'd post my solution to this.
I tried to use ResizeSensor since everyone seemed to have a pretty big crush on it. After implementing though, I realized that under the hood the Element Query requires the element in question to have position relative or absolute applied to it, which didn't work for my situation.
I ended up handling this with an Rxjs interval instead of a straight setTimeout or requestAnimationFrame like previous implementations.
What's nice about the observable flavor of an interval is that you get to modify the stream however any other observable can be handled. For me, a basic implementation was enough, but you could go crazy and do all sorts of merges, etc.
In the below example, I'm tracking the inner (green) div's width changes. It has a width set to 50%, but a max-width of 200px. Dragging the slider affects the wrapper (gray) div's width. You can see that the observable only fires when the inner div's width changes, which only happens if the outer div's width is smaller than 400px.
const { interval } = rxjs;
const { distinctUntilChanged, map, filter } = rxjs.operators;
const wrapper = document.getElementById('my-wrapper');
const input = document.getElementById('width-input');
function subscribeToResize() {
const timer = interval(100);
const myDiv = document.getElementById('my-div');
const widthElement = document.getElementById('width');
const isMax = document.getElementById('is-max');
/*
NOTE: This is the important bit here
*/
timer
.pipe(
map(() => myDiv ? Math.round(myDiv.getBoundingClientRect().width) : 0),
distinctUntilChanged(),
// adding a takeUntil(), here as well would allow cleanup when the component is destroyed
)
.subscribe((width) => {
widthElement.innerHTML = width;
isMax.innerHTML = width === 200 ? 'Max width' : '50% width';
});
}
function defineRange() {
input.min = 200;
input.max = window.innerWidth;
input.step = 10;
input.value = input.max - 50;
}
function bindInputToWrapper() {
input.addEventListener('input', (event) => {
wrapper.style.width = `${event.target.value}px`;
});
}
defineRange();
subscribeToResize();
bindInputToWrapper();
.inner {
width: 50%;
max-width: 200px;
}
/* Aesthetic styles only */
.inner {
background: #16a085;
}
.wrapper {
background: #ecf0f1;
color: white;
margin-top: 24px;
}
.content {
padding: 12px;
}
body {
font-family: sans-serif;
font-weight: bold;
}
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
<h1>Resize Browser width</h1>
<label for="width-input">Adjust the width of the wrapper element</label>
<div>
<input type="range" id="width-input">
</div>
<div id="my-wrapper" class="wrapper">
<div id="my-div" class="inner">
<div class="content">
Width: <span id="width"></span>px
<div id="is-max"></div>
</div>
</div>
</div>
expanding on this answer by #gman, here's a function that allows multiple per element callbacks, exploding the width and height into a quasi event object. see embedded demo that works live here on stack overflow ( you may need to resize the main browser drastically for it to trigger)
function elementResizeWatcher(element, callback) {
var
resolve=function(element) {
return (typeof element==='string'
? document[
['.','#'].indexOf(element.charAt(0)) < 0 ? "getElementById" : "querySelector"
] (element)
: element);
},
observer,
watched = [],
checkForElementChanges = function (data) {
var w=data.el.offsetWidth,h=data.el.offsetHeight;
if (
data.offsetWidth !== w ||
data.offsetHeight !== h
) {
data.offsetWidth = w;
data.offsetHeight = h;
data.cb({
target : data.el,
width : w,
height : h
});
}
},
checkForChanges=function(){
watched.forEach(checkForElementChanges);
},
started=false,
self = {
start: function () {
if (!started) {
// Listen to the window resize event
window.addEventListener("resize", checkForChanges);
// Listen to the element being checked for width and height changes
observer = new MutationObserver(checkForChanges);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
started=true;
}
},
stop : function ( ) {
if (started) {
window.removeEventListener('resize', checkForChanges);
observer.disconnect();
started = false;
}
},
addListener : function (element,callback) {
if (typeof callback!=='function')
return;
var el = resolve(element);
if (typeof el==='object') {
watched.push({
el : el,
offsetWidth : el.offsetWidth,
offsetHeight : el.offsetHeight,
cb : callback
});
}
},
removeListener : function (element,callback) {
var
el = resolve(element);
watched = watched.filter(function(data){
return !((data.el===el) && (data.cb===callback));
});
}
};
self.addListener(element,callback);
self.start();
return self;
}
var watcher = elementResizeWatcher("#resize_me_on_stack_overflow", function(e){
e.target.innerHTML="i am "+e.width+"px x "+e.height+"px";
});
watcher.addListener(".resize_metoo",function(e) {
e.target.innerHTML="and i am "+e.width+"px x "+e.height+"px";
});
var mainsize_info = document.getElementById("mainsize");
watcher.addListener(document.body,function(e) {
mainsize_info.innerHTML=e.width+"px x "+e.height+"px";
});
#resize_me_on_stack_overflow{
background-color:lime;
}
.resize_metoo {
background-color:yellow;
font-size:36pt;
width:50%;
}
<p> resize the main browser window! <span id="mainsize"><span> </p>
<p id="resize_me_on_stack_overflow">
hey, resize me.
</p>
<p class="resize_metoo">
resize me too.
</p>
Pure vanilla implementation.
var move = function(e) {
if ((e.w && e.w !== e.offsetWidth) || (e.h && e.h !== e.offsetHeight)) {
new Function(e.getAttribute('onresize')).call(e);
}
e.w = e.offsetWidth;
e.h = e.offsetHeight;
}
var resize = function(e) {
e.innerText = 'New dimensions: ' + e.w + ',' + e.h;
}
.resizable {
resize: both;
overflow: auto;
width: 200px;
border: 1px solid black;
padding: 20px;
}
<div class='resizable' onresize="resize(this)" onmousemove="move(this)">
Pure vanilla implementation
</div>
With disconnect to remove the event listener:
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "context", "output"]
connect() {
this.inputObserver = new ResizeObserver(() => { this.resizeInput() })
this.inputObserver.observe(this.inputTarget)
}
disconnect() {
this.inputObserver.disconnect(this.inputTarget)
}
resizeInput() {
const height = this.inputTarget.offsetHeight
this.contextTarget.style.height = `${height}px`
this.outputTarget.style.height = `${height}px`
}
}
Only Window.onResize exists in the specification, but you can always utilize IFrame to generate new Window object inside your DIV.
Please check this answer. There is a new little jquery plugin, that is portable and easy to use. You can always check the source code to see how it's done.
<!-- (1) include plugin script in a page -->
<script src="/src/jquery-element-onresize.js"></script>
// (2) use the detectResizing plugin to monitor changes to the element's size:
$monitoredElement.detectResizing({ onResize: monitoredElement_onResize });
// (3) write a function to react on changes:
function monitoredElement_onResize() {
// logic here...
}
i thought it couldn't be done but then i thought about it, you can manually resize a div via style="resize: both;" in order to do that you ave to click on it so added an onclick function to check element's height and width and it worked. With only 5 lines of pure javascript (sure it could be even shorter)
http://codepen.io/anon/pen/eNyyVN
<div id="box" style="
height:200px;
width:640px;
background-color:#FF0066;
resize: both;
overflow: auto;"
onclick="myFunction()">
<p id="sizeTXT" style="
font-size: 50px;">
WxH
</p>
</div>
<p>This my example demonstrates how to run a resize check on click for resizable div.</p>
<p>Try to resize the box.</p>
<script>
function myFunction() {
var boxheight = document.getElementById('box').offsetHeight;
var boxhwidth = document.getElementById('box').offsetWidth;
var txt = boxhwidth +"x"+boxheight;
document.getElementById("sizeTXT").innerHTML = txt;
}
</script>
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.
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.