skip to content

Special Events: The Changes in 1.4.2

5 min read

jQuery’s event system has seen incredible feature growth over the years and with that growth came some pains. jQuery 1.4.2 included a much needed overhaul of the event system. This also brought some backwards incompatible changes to the two new special event hooks added in 1.4. I previously blogged about these two new special event hooks: add and remove. These two events hooks brought lots of power by being able to manipulate the event details for each event handler registered. This is different from the existing setup and teardown hooks (added in 1.2.2) that only worked once per an event, per an element.

I’ve written about Special Events a few times now. I recommend going back and reading the previous posts for more details about Special Events, if you haven’t already done so. First there was the introductory blog post, then the post about the add and remove hooks, and finally a post about automating with special events.

The Changes

First lets rundown the changes which at first may seem more complicated than they actually are. It all has to do with how jQuery internally stores the details related to an event per an element (.data('events')). Since the beginning jQuery stored event details on the handler function itself in an object. Now the event details are stored in an object within an array. This provided several benefits, not the least of these being guaranteed event order in all browsers (which has been a pretty highly desired feature). However, the add and remove callback now get the object with the event details passed instead of the individual arguments as before.

Lets take a look at the two call signatures to make this a little more clear. I’m going to use the same example that I used in the previous blog post about these new event handlers. I’m only showing the add and remove hooks since the setup and teardown are unmodified.

// In jQuery 1.4 and 1.4.1
jQuery.event.special.multiclick = {
    add: function( handler, data, namespaces ) {
        // called for each bound handler
    },

    remove: function( namespaces ) {
        // called for each bound handler
    }
};


// In jQuery 1.4.2+
jQuery.event.special.multiclick = {
    add: function( details ) {
        var handler   = details.handler,
            data      = details.data,
            namespace = details.namespace;
    },

    remove: function( details ) {
        var handler   = details.handler,
            data      = details.data,
            namespace = details.namespace;
    }
};

The Updated multiclick Special Event

If you remember the multiclick event, it had the ability to be configurable per a handler. The usage looks like this.

$('div')
    .bind("multiclick", { threshold: 5 }, function( event ) {
        alert( "Clicked 5 times" );
    })
    .bind("multiclick", { threshold: 3 }, function( event ) {
        alert( "Clicked 3 times" );
    });

The threshold passed in the data for the event handler is how many times the element must be clicked before the event handler fires. Most of the functionality for this special event was in the add hook. It would replace the handler with a handler that would track the number of clicks. Here is what the add hook for this event looked like before.

jQuery.event.special.multiclick = {
    add: function( handler, data, namespaces ) {
        // get the required number of clicks from data
        var threshold = data && data.threshold || 1,
            // number of clicks
            clicks = 0;

        // return a new function that will become the handler
        return function( event ) {
            // increase number of clicks
            clicks += 1;
            if ( clicks === threshold ) {
                // required number of clicks reached, reset
                clicks = 0;
                // call the actual supplied handler
                handler.apply( this, arguments );
            }
        }
    },

    ...
};

And here is how it looks in 1.4.2.

jQuery.event.special.multiclick = {
    add: function( details ) {
        var handler   = details.handler,
            data      = details.data,
            threshold = data && data.threshold || 1,
            clicks    = 0;

        // replace the handler
        details.handler = function(event) {
            // increase number of clicks
            clicks += 1;
            if ( clicks === threshold ) {
              // required number of clicks reached, reset
              clicks = 0;
              // call the actual supplied handler
              handler.apply( this, arguments );
            }
        };
    },

    ...
};

The rest of the special event remains the same.

Support 1.4+

If you need to support 1.4, 1.4.1, and the new way in 1.4.2 then here is some sample code to help you make the transition. This sample code was written by Ben Alman (whom I hear is working on an uber Special Events article).

// 1.4, 1.4.1 and 1.4.2 (existing plugins, update for 1.4.2 this way)
$.event.special.foo = {

  add: function( handleObj ) {
    // Do something when event is bound here!

    var old_handler;

    function new_handler(event) {
      // Modify event object here!
      old_handler.apply( this, arguments );
    };

    // This may seem a little complicated, but it normalizes the special event
    // .add method between jQuery 1.4/1.4.1 and 1.4.2+
    if ( $.isFunction( handleObj ) ) {
      // 1.4, 1.4.1
      old_handler = handleObj;
      return new_handler;
    } else {
      // 1.4.2+
      old_handler = handleObj.handler;
      handleObj.handler = new_handler;
    }
  }

};