Is it possible to simply add event listeners to certain elements to detect if their height or width have been modified? I'd like do this without using something intensive like:
$(window).resize(function() { ... });
Ideally, I'd like to bind to specific elements:
$("#primaryContent p").resize(function() { ... });
It seems like using a resize handler on the window is the only solution, but this feels like overkill. It also doesn't account for situations where an element's dimensions are modified programatically.
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 .
Definition and Usage. The onresize event occurs when the browser window has been resized. Tip: To get the size of an element, use the clientWidth, clientHeight, innerWidth, innerHeight, outerWidth, outerHeight, offsetWidth and/or offsetHeight properties.
I just came up with a purely event-based way to detect element resize for any element that can contain children, I've pasted the code from the solution below.
See also the original blog post, which has some historical details. Previous versions of this answer were based on a previous version of the blog post.
The following is the JavaScript you’ll need to enable resize event listening.
(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);
}
window.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);
};
window.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__);
}
}
}
})();
Here’s a pseudo code usage of this solution:
var myElement = document.getElementById('my_element'),
myResizeFn = function(){
/* do something on resize */
};
addResizeListener(myElement, myResizeFn);
removeResizeListener(myElement, myResizeFn);
http://www.backalleycoder.com/resize-demo.html
Here is a jQuery plugin with watch
and unwatch
methods that can watch particular properties of an element. It is invoked as a method of a jQuery object. It uses built-in functionality in browsers that return events when the DOM changes, and uses setTimeout()
for browsers that do not support these events.
The general syntax of the watch
function is below:
$("selector here").watch(props, func, interval, id);
props
is a comma-separated string of the properties you wish to
watch (such as "width,height"
).func
is a callback function, passed the parameters watchData, index
, where watchData
refers to an object of the form { id: itId, props: [], func: func, vals: [] }
, and index
is the index of the changed property. this
refers to the changed element.interval
is the interval, in milliseconds, for setInterval()
in browsers that do not support property watching in the DOM.id
is an optional id that identifies this watcher, and is used to remove a particular watcher from a jQuery object.The general syntax of the unwatch
function is below:
$("selector here").unwatch(id);
id
is an optional id that identifies this watcher to be removed. If id
is not specified, all watchers from the object will be removed.For those who are curious, the code of the plugin is reproduced below:
$.fn.watch = function(props, func, interval, id) {
/// <summary>
/// Allows you to monitor changes in a specific
/// CSS property of an element by polling the value.
/// when the value changes a function is called.
/// The function called is called in the context
/// of the selected element (ie. this)
/// </summary>
/// <param name="prop" type="String">CSS Property to watch. If not specified (null) code is called on interval</param>
/// <param name="func" type="Function">
/// Function called when the value has changed.
/// </param>
/// <param name="func" type="Function">
/// optional id that identifies this watch instance. Use if
/// if you have multiple properties you're watching.
/// </param>
/// <param name="id" type="String">A unique ID that identifies this watch instance on this element</param>
/// <returns type="jQuery" />
if (!interval)
interval = 200;
if (!id)
id = "_watcher";
return this.each(function() {
var _t = this;
var el = $(this);
var fnc = function() { __watcher.call(_t, id) };
var itId = null;
if (typeof (this.onpropertychange) == "object")
el.bind("propertychange." + id, fnc);
else if ($.browser.mozilla)
el.bind("DOMAttrModified." + id, fnc);
else
itId = setInterval(fnc, interval);
var data = { id: itId,
props: props.split(","),
func: func,
vals: []
};
$.each(data.props, function(i) { data.vals[i] = el.css(data.props[i]); });
el.data(id, data);
});
function __watcher(id) {
var el = $(this);
var w = el.data(id);
var changed = false;
var i = 0;
for (i; i < w.props.length; i++) {
var newVal = el.css(w.props[i]);
if (w.vals[i] != newVal) {
w.vals[i] = newVal;
changed = true;
break;
}
}
if (changed && w.func) {
var _t = this;
w.func.call(_t, w, i)
}
}
}
$.fn.unwatch = function(id) {
this.each(function() {
var w = $(this).data(id);
var el = $(this);
el.removeData();
if (typeof (this.onpropertychange) == "object")
el.unbind("propertychange." + id, fnc);
else if ($.browser.mozilla)
el.unbind("DOMAttrModified." + id, fnc);
else
clearInterval(w.id);
});
return this;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With