Thursday, November 29, 2012

jQuery UI Widgets, Events, and Cleanup

If you're implementing a custom widget, or extending an existing widget, using the widget factory, care must be taken to cleanup when the widget is destroyed. This includes any event handling. The jQuery UI base widget has an _on() function that's inherited by all widgets to help with not only binding event handlers, but with the removal of them as well. That is, you don't need to explicitly unbind the handlers at destroy time if you're using _on() — something I'm apt to forget about.

Let's look at how we would implement a handler, and clean it up once the widget is removed without using _on().

(function( $, undefined ) {

$.widget( "ui.button", $.ui.button, {

    _create: function() {
        this._super( "_create" );
        this.element.click(function ( e ) {
            console.log('clicked');
        });
    },

    _destroy: function() {
        this._super( "_destroy" );
        this.element.unbind( "click" );
    },

});

})( jQuery );

$(function() {
    $( "button" ).button();
});

Here, in our customized version of ui.button, we're registering our handler in _create() and unbinding it in _destroy(). This works, and looks, fine with just one handler. In a more involved widget, we might have several handlers we have to remove in _destroy(). Let's now look at the alternate implementation of the same functionality using _on().

(function( $, undefined ) {

$.widget( "ui.button", $.ui.button, {

    _create: function() {
        this._super( "_create" );
        this._on({
            click: function ( e ) {
                console.log('clicked');
            }
        });
    },

});

})( jQuery );

$(function() {
    $( "button" ).button();
});

You'll notice that in this version, we've removed _destroy() entirely because the event handling cleanup is no longer needed. The _on() function also knows that we're talking about this.element when we ask it to bind events. One final note is that we can use _on() to bind several event handlers for different event names — we just need to state what those are in the object passed in.