jQuery Resize Event

When building web applications, it can be tricky to get pages to layout correctly, especially when layout can't be done with CSS. Widgets that can dynamically change the layout only complicate matters. JavaScriptMVC 3.1 packs a new resize event that greatly simplifies these layouts.

posted in javascriptmvc, in jquery, in open-source, in Development on May 23, 2011 by Justin Meyer

When building web applications, it can be tricky to get pages to layout correctly, especially when layout can’t be done with CSS. Widgets that can dynamically change the layout only complicate matters. JavaScriptMVC 3.1 packs a new resize event that greatly simplifies these layouts.

The Past

In the long long ago, the before time, we would have various controls listening to the window’s resize event and sizing themselves appropriately. But, eventually we discovered several problems or difficulties with this approach:

Problem 1: Callback Ordering

If you look at the demo page, you’ll notice there are several elements that need to be resized:

  • The RED Border element (fills the window)
  • The BLUE Border element (fills the RED Element)
  • The Splitter content elements
  • The Scrollable element in the table

Each of these elements need to be resized after their parent elements have been resized. For example, the BLUE element needs to know the final size of the RED element before calculating its size.

We could be very careful to register and maintain all event handlers in the right order, but this would be tricky, error prone, and downright annoying.

Instead, what’s needed is a resize event that fires in document order. That is, resize is fired first on the window, then the document, and then all child elements from the outside in.

But, this is opposite of how DOM events and jQuery’s synthetic event system (trigger) works. The DOM calculates all the elements between the document and the target and then fires event handlers in two passes:

  • Walks from document to target, firing event handlers listening to the ‘capture’ phase.
  • Walks back from the target to the document, firing event handlers listening to the ‘bubble’ phase.

Bubble

jQuery’s trigger only bubbles. And, even if it supported a capture phase, it would not help. A resize event doesn’t have a single target; instead, every element is a potential resize target!

So, instead of capturing or bubbling, our resize event can be thought of ‘cascading’ the document - firing every resize listener in order from parent to child.

Note If you are worried about this performing … well, don’t. We’ve got it covered.

Problem 2: Widgets That Resize Children.

As the demo … um … demonstrates, we often have widgets (ex: splitters) that can adjust the size of their child elements. We could have these widgets trigger a resize event on the window which would call every resize handler. But, this would be inefficient. Only the elements within our widget might need to adjust themselves.

In the past, we handled this by having child widgets to resize and the parent widget trigger resize on the child widgets:

subElement.triggerHandler('resize');

This worked, but there were cases where subElement wasn’t the element that had the resize event handler.

Instead, we wanted something where we could trigger resize on an element and any of its own or its child elements resize handlers would be called:

subElement.resize()

Problem 3: Browser Differences

There is no standard around the resize event. IE fires resize events on elements, all other browsers only fire a resize event on the window.

Furthermore, it was easy in IE to create recursive resize events that would freeze the browser. You just would have to listen to resize and change the DOM so it would fire another resize event.

Naturally, we fixed all these problems.

Use

Download or steal the jquery/event/resize plugin. Then listen to resize like:

$('#foo').resize(function(){
  // make the element the right size
});

If you didn’t need to resize yourself, you can stopPropagation(), preventing your child elements from getting a resize event:

$('#foo').resize(function(ev){
  if( IdontNeedToResize ){
    ev.stopPropagation()
  }
});

Finally, if you want to call child element’s resize event handlers outside of a window resize event, simply call resize on the child:

child.resize();

How it works

When an element first listens to resize, that element is inserted into array of elements sorted by document order.

When a resize event happens, resize is call on elements in that array. If the outer element being resized is not the document, it only calls event handlers on elements within the outer element.

What about Ben Alman’s Resize Plugin?

Ben’s fantastic resize plugin lets you listen to when an element has been resized by polling for element width and height changes. This is great because you don’t have to call resize if you change the dimensions of an element. However, it has 3 potential drawbacks:

  • Checking width and height is relatively expensive in some browsers. If you have lots of elements listening to resize, this could cause slowness.

  • To create smooth-ish resizing, you need all resize events to fire immediately one after the other. If your outer widget sets the width of an inner element, it could be another 250ms until that element resizes itself and its child elements.

  • Inner elements have to listen to parent resizes.

Ben’s widget is a good choice for pages that don’t have many nested structures that need to be resized.

Conclusion

The resize event has made our lives a lot easier. We’ve created a number of splitter, filler, resizable, and grid like widgets that exploit its power. Hopefully you can use it to clean up this often tricky part of JS application development.

comments powered by Disqus
Contact Us
(312) 620-0386 | contact@bitovi.com
 or cancel