20

What should the best practices to listen on element resize event?

I want to re-position an element (jQuery dialog in my case), once it's size changed. But I am now more interested to do in a general way to to listen to resize event, unaware of how the resize happens. It suppose to be simple until I found an element can be re-sized by

  • window resize
  • content text changes
  • children elements or their children elements resized
  • a sibling element resize (e.g. a cell in a table)
  • JavaScript changes it src(of img)/style attribute directly (or it's child's)
  • JavaScript rewrite CSS rules or stylesheet
  • native resize feature textarea or CSS3 resize
  • browser's zoom or text-enlarge
  • CSS transition or animations (by :hover or any other mean)

In the de-facto standard, there is a event window.onresize to subscribe resize on a window/frame. But there is no a standard event on the HTML content or DOM Elements.

I come across the following thought

MutationObserver is close(inner DOM changes), but it does not (yet) cross browser (IE10 does not support) and it generate noise, not CSS aware.

A naive JavaScript polling should work in all case, but it generate either delay or CPU waste of many poll.

Dennis C
  • 24,511
  • 12
  • 71
  • 99

5 Answers5

7

As of July 2020, ResizeObserver is still un-official in W3C nor WhatWG but it is already supported by all major browsers since support Safari 13.1 since 2020-Mar-24.


FYI, there's a spec for a new ResizeObserver API. Chrome seems to be the only browser that has implemented it as of Aug 2018 (see caniuse.com), but there's at least one polyfill you can use now (which uses MutationObserver).

Dennis C
  • 24,511
  • 12
  • 71
  • 99
clint
  • 14,402
  • 12
  • 70
  • 79
2

Yes there is not simple solution, that's not good.

I've found something very useful for this.: cross browser event based element resize

It's tricky, appending some needed html to the element that have to be listened and detects scrolling event.

Some html example from that page:

<div class="resize-triggers">
    <div class="expand-trigger"><div></div></div>
    <div class="contract-trigger"></div>
</div>

Also Some JS:

var myElement = document.getElementById('my_element'),
    myResizeFn = function(){
        /* do something on resize */
    };
addResizeListener(myElement, myResizeFn);
removeResizeListener(myElement, myResizeFn);

But it works for elements those are able to have children, not for self-closing tags.

You can see the demo http://jsfiddle.net/3QcnQ/67/

George G
  • 7,443
  • 12
  • 45
  • 59
  • only if it helps :))) – George G Sep 05 '14 at 08:00
  • 3
    Where does `addResizeListener` and `removeResizeListener` functions come from? The answer doesn't provide the entire solution. It's a bad practice to hide vital information behind links, because they could break in the future. – Slava Fomin II Apr 22 '19 at 11:10
  • HI @SlavaFominII agree but, I could not just copy whole article from the link though, but I could add it as comment as well. Noted and will consider in the future. Thanks – George G Apr 22 '19 at 18:56
2

Well, there is a easy library for that. Although there's nothing official how to listen on dimension changes of all types of elements and only window supports it at the moment we have luckily a polifill for that that works very accurate and supports all browsers even inclusive IE6+.

https://github.com/marcj/css-element-queries

You can find there a class ResizeSensor. To setup a listener on a element you can just do:

new ResizeSensor($('.myelements'), function() {
    console.log('changed');
});
Marc J. Schmidt
  • 8,302
  • 4
  • 34
  • 33
  • I cannot get this to work on an element contained in a shadowroot. –  Jun 16 '17 at 22:26
  • I can't get this to work either. It does not fire if I change the dimensions of an element using JS or even if the element is responsive and changes dimensions due to window size change – astralmaster Mar 27 '18 at 17:07
1

Given yourelement, when the size changes (ex. a text translation took place) you can doyourstuff(), including
ro.unobserve(yourelement);

  var inilen = yourelement.offsetWidth;
  var ro = new ResizeObserver( entries => {
    for (let entry of entries) {
      const cr = entry.contentRect;
      if (inilen !== cr.width) {
        doyourstuff();        
      }
    }
  });

  ro.observe(<your element>);
0

In the future, we may have the luxury of the ResizeObserver everywhere, but for less recent browsers in 2021 we need to make do with a workaround. This article has already been posted, but it's pretty old and I think the solution might be overly complicated for modern browsers.

Still, the core idea remains: add an <object> element as a child with width: 100%; height: 100%;, and set a resize listener on its inner window object.

Here's some demo code that works in the latest Chrome and Firefox:

const div = document.getElementById('demo')
const obj = document.createElement('object')
obj.className = 'resize-detector'
obj.type = 'text/html'
obj.data = 'about:blank'
obj.addEventListener('load', function() {
  // Initialize once.
  handleResize()
  // Add resize handler on the <object>'s inner window.'
  obj.contentWindow.addEventListener('resize', function() {
    handleResize()
  })
})
div.appendChild(obj)

function handleResize() {
    document.getElementById('size').innerHTML = `${div.offsetWidth}×${div.offsetHeight}`
}
.resizable {
  /* Make this the offset parent of the <object> */
  position: relative;
}

#demo {
  width: 100px;
  height: 100px;
  background-color: #def;
  /* Allow user resizing, for testing. */
  resize: both;
  overflow: hidden;
}

object.resize-detector {
  display: block;
  visibility: hidden;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}
<div id="demo" class="resizable">
  <div id="size"></div>
</div>

It doesn't work inside the StackOverflow snippet because of some same-origin policy shenanigans, but here's a JSFiddle with the same code.

Thomas
  • 174,939
  • 50
  • 355
  • 478