38

Visual Studio highlighted my onresize attribute of my div tag, and says that it isn't a valid attribute for HTML5. Is this true? What should I use instead? It seems kind of silly that this would be the case.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Oztaco
  • 3,399
  • 11
  • 45
  • 84
  • 6
    That article is about the Window's resize event, does it apply to elements like div? And not part of spec doesn't tell me how much browser support it has or if it has a more-valid-on-spec solution or not. – Oztaco Oct 12 '13 at 02:42
  • @leaf68 What are you using to resize the div in the first place? Providing relevant code allows us to help you much much easier – Zach Saucier Oct 12 '13 at 02:57
  • From Javascript, I have the resize event resize several other elements. I'm aware that I could call my resize function each time I set my div's side, but if this way works than it would be much easier because I wouldn't have to remember that each time. – Oztaco Oct 12 '13 at 03:05
  • You can write a function that listens the event of changing width or height of your div. – AxelPAL Oct 17 '13 at 03:19
  • 7
    Thanks @Oztaco for disregarding that condescending comment. Because you posted this question, my "10 seconds of googling" brought me here, to a straightforward answer with discussion and alternatives. +1. – Ben Aug 27 '17 at 17:38

7 Answers7

15

Microsoft's Internet Explorer supports onresize on all HTML elements. In all other Browsers the onresize is only available at the window object. https://developer.mozilla.org/en-US/docs/Web/API/Window.onresize

If you wanna have onresize at a div in all browsers check this:

http://marcj.github.io/css-element-queries/

This library has a class ResizeSensor which can be used for resize detection.

Marc J. Schmidt
  • 8,302
  • 4
  • 34
  • 33
  • I've updated my method, it's now simpler than the previous version and more accurate. Marc, you should update the element queries stuff to use this latest code (backalleycoder is my blog) – csuwldcat Feb 14 '14 at 04:38
  • @csuwldcat, yes it's simpler, but way slower in the initialisation, so I didn't follow the `object` approach any further. – Marc J. Schmidt Mar 02 '16 at 15:53
8

Add the following CSS and JavaScript to your page, and use the addResizeListener and removeResizeListener methods to listen for element size changes (blog post for further details: http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/):

Resize Sensor CSS

.resize-triggers {
    visibility: hidden;
}

.resize-triggers, .resize-triggers > div, .contract-trigger:before {
  content: " ";
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  overflow: hidden;
}

.resize-triggers > div {
  background: #eee;
  overflow: auto;
}

.contract-trigger:before {
  width: 200%;
  height: 200%;
}

Resize Event Methods

The following is the JavaScript you’ll need to enable resize event listening. The first two functions are prerequisites that are used in the main addResizeListener and removeResizeListener methods.

(function(){

var attachEvent = document.attachEvent;

if (!attachEvent) {
    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 resetTriggers(element){
        var triggers = element.__resizeTriggers__,
            expand = triggers.firstElementChild,
            contract = triggers.lastElementChild,
            expandChild = expand.firstElementChild;
        contract.scrollLeft = contract.scrollWidth;
        contract.scrollTop = contract.scrollHeight;
        expandChild.style.width = expand.offsetWidth + 1 + 'px';
        expandChild.style.height = expand.offsetHeight + 1 + 'px';
        expand.scrollLeft = expand.scrollWidth;
        expand.scrollTop = expand.scrollHeight;
    };

    function checkTriggers(element){
      return element.offsetWidth != element.__resizeLast__.width ||
             element.offsetHeight != element.__resizeLast__.height;
    }

    function scrollListener(e){
        var element = this;
        resetTriggers(this);
        if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__);
        this.__resizeRAF__ = requestFrame(function(){
            if (checkTriggers(element)) {
                element.__resizeLast__.width = element.offsetWidth;
                element.__resizeLast__.height = element.offsetHeight;
                element.__resizeListeners__.forEach(function(fn){
                    fn.call(element, e);
                });
            }
        });
    };
}

window.addResizeListener = function(element, fn){
    if (attachEvent) element.attachEvent('resize', fn);
    else {
        if (!element.__resizeTriggers__) {
            if (getComputedStyle(element).position == 'static') element.style.position = 'relative';
            element.__resizeLast__ = {};
            element.__resizeListeners__ = [];
            (element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers';
            element.__resizeTriggers__.innerHTML = '<div class="expand-trigger"><div></div></div>' +
                                                   '<div class="contract-trigger"></div>';
            element.appendChild(element.__resizeTriggers__);
            resetTriggers(element);
            element.addEventListener('scroll', scrollListener, true);
        }
        element.__resizeListeners__.push(fn);
    }
};

window.removeResizeListener = function(element, fn){
    if (attachEvent) element.detachEvent('resize', fn);
    else {
        element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
        if (!element.__resizeListeners__.length) {
            element.removeEventListener('scroll', scrollListener);
            element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__);
        }
    }
}

})();

Demo-licious!

Here’s a pseudo code usage of the method.

var myElement = document.getElementById('my_element'),
    myResizeFn = function(){
        /* do something on resize */
    };
addResizeListener(myElement, myResizeFn);
removeResizeListener(myElement, myResizeFn);
csuwldcat
  • 8,021
  • 2
  • 37
  • 32
  • Doesn't seem to work for me: http://jsfiddle.net/t2Aqa/7/ The resize event should be called either by pressing the toggle button or resizing the area in which the HTML is rendered but it seems to be only called once after the layout is done. Did I miss something? – suamikim May 14 '14 at 09:32
  • 1
    @suamikim Yeah, in your addResizeListener handler, you're blowing away the sensor element the method injects to listen for resize by overwriting all the contents of the target `target.html(target.width() + 'x' + target.height());` – csuwldcat May 14 '14 at 23:13
  • 3
    @csuwldcat If you're going to copy and paste an answer from another resource, at least give it a reference and an acknowledgement: http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/ – Jamie Barker Jan 04 '18 at 09:28
  • 1
    @JamieBarker you're right, I feel bad for shamelessly stealing from a guy who is obviously a genius. I bet he was more concerned with the content being posted here to help others and simply forgot to include a link to his blog. I looked him up to send an apology, and it turns out he's almost as handsome as I am: https://github.com/csuwildcat. Hell of a guy, if you ask me;) – csuwldcat Jan 06 '18 at 04:23
  • Haha! Alright then if it it's you then at least put a reference so it doesn't look like you're plaguerising ;) – Jamie Barker Jan 09 '18 at 13:03
  • 1
    @JamieBarker added the link to my post. – csuwldcat Jan 13 '18 at 19:17
8

Only Window.onResize exists in the specification. Please remember that every IFrame element creates new Window object which supports onResize. Therefore IMO the most reliable method to detect changes to the element's size is to append hidden iframes to the element.

If you are interested in a neat and portable solution, please check this plugin. It takes 1 line of code to listen the event of changing width or height of your div.

<!-- (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...
}
rbtbar
  • 87
  • 1
  • 5
8

We now have ResizeObserver, browser support is 89.6% today (2021-01-21).

And there is polyfill for old browsers, like juggle/resize-observer

Isaac Corbrey
  • 553
  • 3
  • 20
Nate Scarlet
  • 551
  • 5
  • 7
5

A pure and simple JavaScript implementation.

.resizable {
  resize: both;
  overflow: auto;
  width: 200px;
  border: 1px solid black;
  padding: 20px;
  margin-bottom: 120px;
}
<div class='resizable' onresize="this.innerText = this.w + ',' + this.h" onmousemove="if ((this.w && this.w !== this.offsetWidth) || (this.h && this.h !== this.offsetHeight)) { eval(this.getAttribute('onresize')) } this.w = this.offsetWidth; this.h = this.offsetHeight;">
test
</div>



Paul H.
  • 387
  • 4
  • 8
  • +1 Very useful method, but contains some black magic. Would be clearer with js functions for onresize and onmousemove – Roland Dec 10 '22 at 19:48
  • Some "black magic" was removed in my [**adapted** version of this approach](https://stackoverflow.com/a/75836163/9064287). – nhaggen Mar 24 '23 at 16:38
2

Currently all major browser doesn't support a resize event for html-element's, just for the window object.

What you can to is to intercept the user interaction to build your own listener, like this:

function detectResize(_element)
{
    let promise = {};
    let _listener = [];

    promise.addResizeListener = function(listener)
    {
        if(typeof(listener) != "function") { return; }
        if(_listener.includes(listener)) { return; };

        _listener.push(listener);
    };

    promise.removeResizeListener = function(listener)
    {
        let index = _listener.indexOf(listener);
        if(index >= 0) { _listener.splice(index, 1); }
    };

    let _size = { width: _element.clientWidth, height: _element.clientHeight };

    function checkDimensionChanged()
    {
        let _currentSize = { width: _element.clientWidth, height: _element.clientHeight };
        if(_currentSize.width != _size.width || _currentSize.height != _size.height)
        {
            let previousSize = _size;
            _size = _currentSize;

            let diff = { width: _size.width - previousSize.width, height: _size.height - previousSize.height };

            fire({ width: _size.width, height: _size.height, previousWidth: previousSize.width, previousHeight: previousSize.height, _element: _element, diff: diff });
        }

        _size = _currentSize;
    }

    function fire(info)
    {
        if(!_element.parentNode) { return; }
        _listener.forEach(listener => { listener(info); });
    }


    let mouseDownListener = event =>
    {

        let mouseUpListener = event => 
        {
            window.removeEventListener("mouseup", mouseUpListener);
            window.removeEventListener("mousemove", mouseMoveListener);
        };

        let mouseMoveListener = event =>
        {
            checkDimensionChanged();
        };

        window.addEventListener("mouseup", mouseUpListener);
        window.addEventListener("mousemove", mouseMoveListener);
    };

    _element.addEventListener("mousedown", mouseDownListener);

    window.addEventListener("resize", event =>
    {
        checkDimensionChanged();
    });

    return promise;
}

How to use it:

document.addEventListener("DOMContentLoaded", event =>
{
    let textarea = document.querySelector("textarea");
    let detector = detectResize(textarea);

    let listener = info => { console.log("new width: ", info.width, "  new height: ", info.height); };
    detector.addResizeListener(listener);
});

html:

<textarea></textarea>

css:

html, body
{
    height: 100%;
    box-sizing: border-box;
    overflow: hidden;
    margin: 0px;
}

textarea
{
    resize: both;

    width: 96px;
    height: 112px;

    width: 100%;
    height: 100%;

    border: 1px solid black;
}
Martin Wantke
  • 4,287
  • 33
  • 21
0

Based on Paul H.'s answer, I created a very similar solution that is easier to understand (at least for me).

The advantage of this modified solution, is that the code is more readable, especially if you want to perform some lines of code within the EventListener.

The advantage of the original solution, is of course that it is a one-liner.

let myDiv = document.getElementById("myDiv");
    myDiv.addEventListener("mousemove", () => {
        if ((myDiv.w && myDiv.w !== myDiv.offsetWidth) || (myDiv.h && myDiv.h !== myDiv.offsetHeight)) {
            // Put your payload-code within this IF-Statement. e.g.
            myDiv.innerText = "New dimensions are W: " + myDiv.w + "  , H: " + myDiv.h;
        }
        myDiv.w = myDiv.offsetWidth;
        myDiv.h = myDiv.offsetHeight;
    });
nhaggen
  • 301
  • 2
  • 8