Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect if an element has been resized via javascript?

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.

like image 604
Jim Jeffers Avatar asked Feb 13 '12 03:02

Jim Jeffers


People also ask

How do you check if a div is resized?

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 .

What is resize method in Javascript?

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.


2 Answers

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__);
      }
    }
  }
})();

Usage

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

Demo

http://www.backalleycoder.com/resize-demo.html

like image 134
csuwldcat Avatar answered Oct 16 '22 05:10

csuwldcat


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;
}
like image 4
kevinji Avatar answered Oct 16 '22 06:10

kevinji