Home > OS >  Invoke function after div resize is complete?
Invoke function after div resize is complete?

Time:08-17

I have a resizeable div like this:

div{
  resize:both;
  overflow:hidden;
  height:10rem;
  width:10rem;
  border:solid 0.5rem black;
}
<div id="item" ></div>

How do I invoke a function after it has completed resizing?

i.e. I don't want the function to continuously invoke while it's being resized,
rather I need it to invoke after the mouse has been lifted from the bottom right resizing icon enter image description here

CodePudding user response:

You could just check the mouseup event, and track the old v new size.

The only gotcha here is if there are other ways to resize the div other than using the mouse.

const item = document.querySelector('#item');

function getSize() {
  return {
    w: item.offsetWidth,
    h: item.offsetHeight
  }
}

let old = getSize();

item.addEventListener('mouseup', () => {
  let n = getSize();
  if (old.w !== n.w && old.h !== n.h) {
    console.log('resized: '   JSON.stringify(n));
    old = n;
  }
});
#item {
  resize:both;
  overflow:hidden;
  height:10rem;
  width:10rem;
  border:solid 0.5rem black;
}

.as-console-wrapper {
  max-height:50px!important;
}
<div id="item" ></div>

CodePudding user response:

What I meant by combining a ResizeObserver and a debouncer is something like this:

const observerDebouncers = new WeakMap;
const observer = new ResizeObserver(entries => {
    
  entries.forEach(entry => {
    
    // Clear the timeout we stored in our WeakMap if any
    clearTimeout( observerDebouncers.get( entry.target ) );
    // Set a new timeout to dispatch an event 200 ms after this resize
    // if this is followed by another resize, it will be cleared by the above.
    observerDebouncers.set( entry.target, setTimeout(() => {
      
      entry.target.dispatchEvent( new CustomEvent( 'resized' ) );
      
    }, 200) );
    
  })
  
});

const resizeable = document.querySelector( 'div' );

resizeable.addEventListener( 'resized', event => {
  
  console.log( 'resized' );
  
});

// Register the resize for this element
observer.observe( resizeable );
#item {
  resize:both;
  overflow:hidden;
  height:10rem;
  width:10rem;
  border:solid 0.5rem black;
}
<div id="item" ></div>

A little bit of a breakdown: observerDebouncers just contains a weakly mapped list from a target to the current timeout stored. It's weakly bound so if you ever remove the element, the timeout id is automatically lost. This means you never really have to worry about the memory consumption here becoming unwieldy.

The observer itself just creates a timeout for every resize event it receives. It also cancelled the previously stored timeout. This means that as long as there is continuous dragging, the timeout will get cancelled and the resized event delayed by 200 more milliseconds. If the resizing stops, the event will fire 200 milliseconds later. (Thats what's called a debouncer, its very common logic in JS to limit the amount of actual events being fired).

The last thing you then need to do is listen for the event and add the target to the observer and voila, a resized event.

This seems like a lot of boilerplate, but it's rather efficient. The only improvement you could make would be to register a single event handler for every target (again, weakly), which would prevent a new function being created every call. A little bit like this:

const observerDebouncers = new WeakMap;
const observerHandlers = new WeakMap;
const observer = new ResizeObserver(entries => {
    
  entries.forEach(entry => {
    
    // This will only happen once, so only one method per target is ever created
    if( !observerHandlers.has( entry.target ) ){
      
      observerHandlers.set( entry.target, () => {
      
        entry.target.dispatchEvent( new CustomEvent( 'resized' ) );

      });
      
    }
    
    // Same as before, except we reuse the method
    clearTimeout( observerDebouncers.get( entry.target ) );
    observerDebouncers.set( entry.target, setTimeout( observerHandlers.get( entry.target ), 200 ) );
    
  })
  
});

const resizeable = document.querySelector( 'div' );

resizeable.addEventListener( 'resized', event => {
  
  console.log( 'resized' );
  
});

observer.observe( resizeable );
#item {
  resize:both;
  overflow:hidden;
  height:10rem;
  width:10rem;
  border:solid 0.5rem black;
}
<div id="item" ></div>

  • Related