511

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:

Before Resize enter image description here

<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>
William X
  • 6,751
  • 6
  • 31
  • 50
  • 11
    This will not work because, you're binding resize event to specified div element. But resize event will trigger for the window not for your element. – Fatih Acet Jun 27 '11 at 12:57
  • You could use `setInterval` here as a possible solution. You can always bind `clearInterval` to a button click to stop the loop once your work is done. –  Nov 24 '16 at 18:53
  • 10
    Since 2020, *ResizeObserver* works in all major browsers (Chrome, Safari, Firefox, Edge) except IE. https://caniuse.com/#feat=resizeobserver – Dan May 04 '20 at 13:29
  • 1
    It's possible to simulate the resize event by using a 3-line function. Please, check [this answer](https://stackoverflow.com/a/67089559/2457251) out for a runnable example. – Almir Campos Apr 14 '21 at 10:13

28 Answers28

586

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

Bogdan D
  • 5,321
  • 2
  • 31
  • 32
Daniel Herr
  • 19,083
  • 6
  • 44
  • 61
  • 3
    not yet useful for production sites, since only Chrome supports it at the moment. But definitely the best solution here! btw, here is a quick overview of implementation status: https://www.chromestatus.com/feature/5705346022637568 – Philipp Sep 10 '16 at 19:47
  • 9
    @Philipp There is a polyfill. – Daniel Herr Sep 15 '16 at 23:35
  • 2
    It seems it's still an experimental feature in all browsers http://caniuse.com/#feat=resizeobserver – Francesc Rosas Aug 10 '17 at 19:35
  • Enabled per default since Chrome 64: https://www.chromestatus.com/feature/5705346022637568 – ggradnig Jan 26 '18 at 16:16
  • Here's a demo of using ResizeObserver to create a custom element that fires `resize` events when it's dimensions change: https://ebidel.github.io/demos/dom_resize_events.html – ebidel Feb 13 '18 at 00:47
  • 1
    anybody who wants to use this feature on electron use webPreferences: { experimentalFeatures: true } – m4heshd Apr 14 '18 at 09:59
  • 5
    Even 2 years after this answer, browser support is bleak: https://caniuse.com/#search=resizeobserver – Scrimothy Dec 18 '18 at 17:39
  • The Firefox and Safari issues have recently been resolved; looks liker wider support is coming soon. I'm electing to use this feature instead of the excellent library in the accepted answer. – JoshuaCWebDeveloper Apr 29 '19 at 20:19
  • The support for this feature is still very limited so use it at your own risk! https://caniuse.com/resizeobserver – Andris Jun 24 '19 at 11:15
  • The polyfill being mentioned above is here: https://github.com/que-etc/resize-observer-polyfill I use it in the "ponyfill" mode (`import ResizeObserver from 'resize-observer-polyfill';`) and it works pretty well. – Andrei Sinitson Sep 02 '19 at 06:01
  • After playing more with it I found out that `resize-observer-polyfill` does not work well with current versions of Firefox (I tried 66 and 68). When I try to use it I get unresponsive page. This one works okay in both Firefox 66, 68: https://github.com/juggle/resize-observer – Andrei Sinitson Sep 03 '19 at 12:54
  • 3
    Works like a charm in the latest Chrome and FF. – Klemen Tusar Oct 22 '19 at 12:08
  • 8
    Works in Firefox, chrome, safari, Opera now. – explorer Nov 11 '19 at 04:28
  • 51
    ResizeObserver is now supported in all major browsers now. – Useful Angle May 11 '20 at 08:10
  • @UsefulAngle exept in firefox mobile – Benny Alex Aug 20 '20 at 18:06
  • This is for textbox, not div. – Cybernetic Aug 18 '21 at 14:52
  • 2
    @Cybernetic Resize Observer is not specific to textboxes, it works for any elements including divs. – Daniel Herr Sep 10 '21 at 01:10
  • 1
    It's 2022 and it's supported in all major browsers. – Anas Latique Aug 17 '22 at 09:11
296

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.

alexandre-rousseau
  • 2,321
  • 26
  • 33
Marc J. Schmidt
  • 8,302
  • 4
  • 34
  • 33
  • I know this is old, but this answer is not good at all. This is not a way to detect if an element size has changed. I want to fire a bunch of javascript if an element takes up the entire witdth of the screen, all the library you linked to makes things look nicer as the size changes , it does not fire any events when the size changes – Scott Selby Oct 12 '15 at 15:47
  • 3
    @ScottSelby Did you try the ResizeSensor class? It seems like you can use it to listen to size changes and then apply your own logic. – Alex Dec 18 '15 at 14:32
  • 2
    `requestAnimationFrame` runs every few milliseconds. Since there doesn't seem to be a real solution to the problem, I'd personally prefer an inaccurate `setTimeout/setInterval` that could run much more infrequently. The dirty checking is a nice approach, though. – Aletheios Mar 17 '16 at 18:27
  • 6
    @Aletheios I guess you miss a point: setTimeout is not only inaccurate but slow as hell. A requestAnimationFrame that checks for a simple boolean is orders of magnitude faster than a setTimeout checking all the time against DOM properties. Beside that setTimeout is also called every few milliseconds. – Marc J. Schmidt Mar 17 '16 at 19:38
  • 2
    @owen, from all kind of trigger. If the div size changes, it will detect it, no matter the reason was. – Marc J. Schmidt Aug 05 '16 at 12:52
  • @MarcJ.Schmidt please show us where jQuery uses a setTimeout to detect resize events, because I've checked back to 1.8.1 and it's purely a DOM based event. Besides which, if anything you would want to debounce resize events, not handle them faster. – Orwellophile Aug 13 '16 at 06:11
  • 6
    @Orwellophile, "purely a DOM based event", there's no DOM based solution in jQuery because there's simply only for `window` object an event called `resize`. setTimeout is slow, if they switched to window.resize event then it does not handle all cases an resize event can occur (like animations, innerHTML change, css changes, etc) – Marc J. Schmidt Aug 19 '16 at 13:01
  • @Marc, I found out after posting that only windows get resize events, but I've checked every instance of setTimeout in the source, and the only time it's used outside of a method to async launch some code, is in AJAX timeouts. `resize` appears to be handled by the generic jQuery event handler, which disclaims _jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding_ – Orwellophile Aug 19 '16 at 13:38
  • 3
    @Orwellophile dude, in his answer he says 'jQuery onresize plugin as it uses setTimeout()', note the *onresize plugin* bit. jQuery does not have a native element resize, just a window one. See [onresize](https://github.com/cowboy/jquery-resize/blob/master/jquery.ba-resize.js) for what he's talking about – Guy Sep 07 '16 at 16:05
  • 10
    css-element-query: uses a real resize event that is triggered everytime the dimension changes. Since you'd get too many events (for drag'n'drop for example) it added requestAnimationFrame checking for a simple boolean variable to smooth those triggered events down to 1/frame. jquery plugin: Uses a setTimeout checking all the time DOM layout props (clientHeight etc) where each check is very slow but needs to be checked often to have same functionality. – Marc J. Schmidt Sep 27 '16 at 14:01
  • Upvoted for "jQuery onresize plugin uses setTimeout() loop to check for changes" – KittenCodings Oct 07 '16 at 00:53
  • So if we need to reposition another element as a result a `
    ` resize, what would be the best way? Use jQuery's on resize?
    – Ben Guild Nov 14 '16 at 07:28
  • 1
    Also, as the **promotion** (spam) page says: "Don't tell - show! The best way to avoid being seen as a snake-oil salesman is **to demonstrate a solution rather than simply asserting the problem can be solved**." You are not showing anything. – Camilo Terevinto Jan 15 '17 at 15:32
  • 2
    **Amazing little tool!!!** Just added the three lines `` and my containing iframe resized on every child event (a complex AngularJS app) absolutely reliably. Huge thanks @Marc Schmidt, as easy to use as the github homepage is easy to look at (although why doesn't it have this example?). – Chris Jan 24 '17 at 20:37
  • You shouldn't use `getComputedStyle`, use `offsetWidth` and `offsetHeight` instead, as they are faster. – Somnium Jan 26 '17 at 11:30
  • Also you shouldn't use `style.cssText = ...` as it does nothing according to specification (https://www.w3.org/TR/cssom-1/#changes-from-5-december-2013). – Somnium Jan 26 '17 at 11:38
  • 1
    @MarcJ.Schmidt -- *"Since you'd get too many events (for drag'n'drop for example) it added requestAnimationFrame checking for a simple boolean variable to smooth those triggered events down to 1/frame"* -- So, in essence, it's a kind of v-sync throttling or debouncing _on top_ of actual real event occurrences. Right? – John Weisz Feb 06 '17 at 11:08
  • 3
    @JohnWeisz exactly. – Marc J. Schmidt Feb 06 '17 at 12:36
  • 11
    @Aletheios, @Orwellophile, and anyone who may have been mislead by them: `requestAnimationFrame` vs `setTimeout` is irrelevant, this library _**doesn't loop**_. `requestAnimationFrame` is only used to debounce/throttle frequency of calling your callbacks, it _**doesn't poll**_. MutationObserver could theoretically be used to similar effect but not in older browsers, unlike this. This is outstandingly clever. – Han Seoul-Oh Apr 26 '17 at 02:29
  • 2
    I'm not usually impressed by code but this is excellent, both the code itself and performance. – Dovev Hefetz Apr 12 '18 at 12:28
  • this lib is nice, though it's a shame its adding an inline style of `position: relative` attribute – Sagiv b.g Jun 19 '18 at 08:11
  • If you apply your solution to a list of elements, where each element has its own ResizeSensor attached to it, will this be a penalty in performance? Or is there a way to delegate a single ResizeSensor to multiple elements? – Johann Jul 22 '18 at 08:55
  • @AndroidDev, the more sensors you have, the slower it gets. There's no way to delegate a single sensor to multiple events, except if you can attach the sensor to the parent of your elements (which should be resized as well when one of its children changes its dimensions), this would end up with only one sensor, which is way faster than having multiple sensor for each child element. – Marc J. Schmidt Jul 23 '18 at 14:58
  • Sorry, I'm still wondering why you mentioned that jquery plugin relies on setTimeout and that's not efficient, while the plugin uses setInterval and setTimeout too. – Carlos J García Jun 12 '19 at 19:09
  • 2
    @CJGarcía setTimeout per se is not bad, but what you do in that setTimeout matters. Most resize-checkers are implemented in a way that a function via setTimeout is executed every few milliseconds checking for dimension changes. That happens via reading `clientHeight`, `clientWidth` on the DOM element, which is extremely slow as it triggers a layout calculation in the rendering engine. My library instead uses setTimeout to check against a simple boolean variable (that is set via an event based resize detector) which is probably a few million times faster. – Marc J. Schmidt Dec 12 '19 at 15:51
  • I wonder how no one has mentioned this: https://www.npmjs.com/package/resize-observer-polyfill. Am I missing something? – JeB May 19 '20 at 17:49
  • @JeB This answer is 7 years old. However, your linked solution does not support all cases where a dimension change can happen. They use a very basic resize strategy which is not as powerful as the linked one, although it has some limitations. The linked one uses the scroll strategy which detects all changes, has however some limitations. See their readme. – Marc J. Schmidt May 19 '20 at 18:07
  • i did not test; but if i understand correct this plugin relays on scroll event. If that is the case, i guess it can't monitor size change of a container that has overlow:scroll enabled. (not interested in resize of the content, but in the container itself). Of corse a wrapper around the scrollcontainer could fix this. But in that case i prefer a dummy iframe. That will trigger resize events as well. and you do not have to reset the scroll position every time. – Doomic Jul 08 '20 at 13:52
  • @Doomic it can detect changes also for a container with `overflow: scroll` – Marc J. Schmidt Jul 08 '20 at 17:35
  • a, now i see.. it is positioned absolute. So it keeps the size of the container instead of the content. Great job. Still think adding a dummy iframe and relay on browser event is cleaner. But i must say you did a nice job with this lib. – Doomic Jul 12 '20 at 11:33
  • @MarcJ.Schmidt , Tested with div elements and works great! (Didn't worked with textarea elements though!) – User May 29 '21 at 23:27
53

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
  });
};
Neuron
  • 5,141
  • 5
  • 38
  • 59
nkron
  • 19,086
  • 3
  • 39
  • 27
  • 1
    Unfortunately, it seems MutationObserver can't detect changes in Shadow DOM. – Ajedi32 Oct 27 '15 at 14:51
  • @nkron, Unfortunately, if you use [CSS3 resize](http://stackoverflow.com/a/31510003/632951), resize can happen when the user resizes manually. This event can't be caught. – Pacerier Feb 19 '16 at 01:38
  • @Ajedi32 Yes, but you can use MutationObserver inside of the ShadowDOM. What I mean, is instead of watching the `body` as in this example, you can watch an element directly. That is actually more performant and efficient than watching the whole body. – trusktr Aug 06 '16 at 06:58
  • @Ajedi32 Well, the only problem is, if the Shadow DOM tree is closed, then you won't be able to access the internals, but I don't think that is usually something you need to do, and if you need to observe a closed tree like that then there may be a problem in the design. Ideally, you should only need to observe your own elements which you have access to. A Shadow-DOM component that requires you to observe it's internal sizing may not be properly designed. Do you have a specific example? If so, maybe I can help suggest a solution (because I'm interested in it too, but have no example yet). – trusktr Aug 06 '16 at 07:02
  • @trusktr I don't have a MCVE at the moment, but basically any Shadow DOM element which can change its size as a result of some user input would be enough to trigger this issue. A box that expands with some additional information when clicked, for example. Note that I'm observing the page, not the individual element, because I want to automatically scroll down when the size of the page increases, regardless of reason. A Shadow DOM element expanding is just one of many possible reasons why the page might get longer. – Ajedi32 Aug 08 '16 at 13:40
  • @Ajedi32 Well, Shadow DOM exists exactly for the reason to prevent you from accessing it, so I don't really understand your point. If you want to observe if a web component has changed its size, you would not try to observe changes of its inner elements. Instead you would put it into another element, like a DIV - something where you have access to, and observe it instead. Or did I misunderstood you? – StanE Jan 01 '17 at 17:52
  • @user25163 That doesn't work either. Even if the parent DIV's size changes, the MutationObserver doesn't detect that if the size changed as a result of something in the ShadowDOM changing. – Ajedi32 Jan 01 '17 at 18:49
  • The second function is working great for me on Vue project, simplest & lightest from all other solutions. – digout Jun 27 '19 at 12:33
46

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.

GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
  • 10
    The only problem with this one is it doesn't report a change unless a mutation actually happens to the element. If the element resizes due to other elements being added, this won't work. – Tony K. Apr 13 '18 at 20:40
  • I agree with @TonyK., this is my only problem with MutationObserver and now I am looking for an element resize library – Murhaf Sousli Jun 08 '19 at 03:16
  • @JerryGoyal It is not working when the div contains tag. In the first instance the image is not loaded and the div size is set as 0 and after load the div size is changed, but observer is not triggered – Santhosh Jan 21 '20 at 09:18
  • for manually changing the size, perfect solution. However, as my div is also draggable, it also files the size change when you drag it over the screen. – Ewald Bos Apr 29 '22 at 13:12
  • Thanks for the input, folks, I've updated the answer to use a more accurate `ResizeObserver` method instead – GorvGoyl Aug 04 '22 at 11:35
  • you save my life, my hero) – Asadbek Eshboev Apr 10 '23 at 12:25
37

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);
});
Neuron
  • 5,141
  • 5
  • 38
  • 59
Martin Wantke
  • 4,287
  • 33
  • 21
  • This is what charts js do. – Antoniossss Jan 17 '18 at 13:12
  • Thanks for compressing that. I'm going to try it out :) – Tony K. Apr 13 '18 at 20:40
  • There's something missing in this expression `parseInt( getComputedStyle(element) )` – GetFree May 07 '18 at 18:41
  • perhaps you should debounce or use requestAnimationFrame to reduce the hit? I think scroll can trigger many events. – mr1031011 Nov 13 '18 at 07:20
  • 2
    I'm confused how is "let zIndex = parseInt(getComputedStyle(element));" valid? I assume you meant: "let zIndex = parseInt(getComputedStyle(element).zIndex); – Ryan Badour Apr 01 '19 at 15:48
  • 1
    The solution/code above does not detect all resize events (e.g. if a container is resized to a fixed with it does not detect the resize for child events. See http://jsfiddle.net/8md2rfsy/1/). The accepted answer works (uncomment the respective lines). – newBee Apr 29 '19 at 14:05
30

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.

Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
  • 155
    this answer is not good enough. size changes might occur without ANY window resize. at all. – vsync Mar 16 '14 at 00:02
  • 9
    for example when you have a splitter element or just append some text or other content to an element. – Günter Zöchbauer May 03 '14 at 17:02
  • @anu not true, you can have eg DOM modification via javascript causing CSS to apply to new structure resulting in different layout - size of every container. – Antoniossss Jan 17 '18 at 13:30
16

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

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.

Stan
  • 2,151
  • 1
  • 25
  • 33
  • +1 for element queries but I'm not hopeful about seeing them anytime soon. There isn't even a draft spec at this point AFAIK... maybe one of us interested parties should put one together and submit it? – Rob Evans Jul 14 '15 at 14:42
  • 1
    That's a good idea of course. It will take a long time to reach a final version, a lot of attention needs to be drawn. Designing this in a good way does not seem trivial to me. Cross-browser compatibility, circular dependencies, debugging, readability and maintenance etc. So let's start as early as possible :) – Stan Jul 17 '15 at 10:00
16

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>
joshcomley
  • 28,099
  • 24
  • 107
  • 147
  • 1
    This is it. 147 lines, uses scroll events. Read through this code if you want the full and true answer on how to best detect div dimension changes. Hint: it's not "just use this library." – Seph Reed Oct 30 '16 at 20:22
  • 1
    People should take a look at [this issue](https://github.com/sdecima/javascript-detect-element-resize/issues/42) before they try out this library. – Louis Nov 17 '16 at 19:34
11

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();
});
Bharat Patil
  • 1,398
  • 1
  • 11
  • 28
  • Interesting. That jsfiddle consistently crashes chrome with the inspector is open and I click the "Add more text" link. Firefox throws "too much recursion" errors. – EricP Sep 22 '13 at 17:58
  • 3
    Please do not use jQuery's resize plugin. It's really slow and not accurate as well. See my answer to get a better solution. – Marc J. Schmidt Oct 17 '13 at 04:03
  • 24
    This does NOT directly detect dimension changes. it's just a by-product of a resize event listener. a dimension changes can happen in many other ways. – vsync Mar 16 '14 at 00:04
  • 4
    What if the resize of the div is from change in style or dom and not by window resize? – awe Apr 10 '15 at 11:46
9

You can use iframe or object using contentWindow or contentDocument on resize. Without setInterval or setTimeout

The steps:

  1. Set your element position to relative
  2. Add inside an transparent absolute hidden IFRAME
  3. 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:

Aminadav Glickshtein
  • 23,232
  • 12
  • 77
  • 117
  • 1
    A note for those not using jquery, `IFRAME.contentWindow` is not available until the `onload` event fires on the iframe. Also, you may want to add the style `visibility:hidden;`, as the iframe can block mouse input to elements behind it (seems to be "floating" in IE at least). – James Wilkins Jun 16 '16 at 05:00
  • 3
    Although this works, it is a heavy-weight solution with the overhead of creating a whole new browsing context just to observe the size change. Also, in some cases it is not ideal or feasible to change the `display` property of the element. – trusktr Aug 06 '16 at 07:05
  • agreed, this is a terrible solution as stated above – 29er Nov 15 '16 at 19:20
  • How wrote so terrible solution? – Aminadav Glickshtein Nov 15 '16 at 19:47
  • this method works well with video players and audio players that need dynamic content resize. Im using this method to resize wavesurfer.js output. – David Clews Jan 24 '20 at 14:14
9

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>
Aikon Mogwai
  • 4,954
  • 2
  • 18
  • 31
9

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.

Pointy
  • 405,095
  • 59
  • 585
  • 614
7

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

Trem
  • 100
  • 1
  • 3
5

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);
});
Zzarcon
  • 259
  • 1
  • 3
  • 9
4

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();
Gman
  • 761
  • 5
  • 6
3

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.

Ben Gripka
  • 16,012
  • 6
  • 45
  • 41
2

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
}); 
Louis
  • 146,715
  • 28
  • 274
  • 320
Dustin Poissant
  • 3,201
  • 1
  • 20
  • 32
  • 1
    Your link was pointing to a location that no longer exists. I've searched for the name of the plugin and found a page that corresponds to your description. Since you appear to be the author of the plugin, I've also edited the language of your answer to make it explicit that this is your work. The rules of this site are such that when you write answers recommend using something you've developed you *must* make the relationship explicit. – Louis Nov 17 '16 at 19:29
  • This is not great solution because you run some function in setTimer over and over again which means the browser is constantly burning CPU even if nothing is happening. Laptop and cell phone owners benefit from solution that uses browser events only when there is some action, like using the onScroll event. – Roman Zenka Jan 23 '18 at 16:26
2

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

Community
  • 1
  • 1
2

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);
    }
Seph Reed
  • 8,797
  • 11
  • 60
  • 125
2

Pure Javascript solution, but works only if the element is resized with the css resize button:

  1. store element size with offsetWidth and offsetHeight;
  2. add an onclick event listener on this element;
  3. when triggered, compare curent offsetWidth and offsetHeight with stored values, and if different, do what you want and update these values.
roipoussiere
  • 5,142
  • 3
  • 28
  • 37
0
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);

});
Fábio Zangirolami
  • 1,856
  • 4
  • 18
  • 33
0

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;
});
0xe1λ7r
  • 1,957
  • 22
  • 31
0

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>
Scrimothy
  • 2,536
  • 14
  • 23
0

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&nbsp;x&nbsp;"+e.height+"px";
});

watcher.addListener(".resize_metoo",function(e) {
  e.target.innerHTML="and i am "+e.width+"px&nbsp;x&nbsp;"+e.height+"px";
});

var mainsize_info = document.getElementById("mainsize");

watcher.addListener(document.body,function(e) {
  mainsize_info.innerHTML=e.width+"px&nbsp;x&nbsp;"+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>
unsynchronized
  • 4,828
  • 2
  • 31
  • 43
0

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>
Paul H.
  • 387
  • 4
  • 8
0

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`
  }
}
Dorian
  • 7,749
  • 4
  • 38
  • 57
-1

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...
}
Community
  • 1
  • 1
rbtbar
  • 87
  • 1
  • 5
-1

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>
Marc Stober
  • 10,237
  • 3
  • 26
  • 31
PixelPimp
  • 75
  • 1
  • 7
  • 2
    This only calculates when a click event is fired which is not part of the requirement of the question. – Rob Evans Jul 14 '15 at 14:39