skip to content

Special Events

5 min read

jQuery, since 1.2.2, has had an API for “special events”. These events are special because they have the ability to do some extra work for specific events and even the option to bypass some of the internal jQuery event system. With these special events you can create custom events that require some setup work or you can completely overwrite the behavior of native events.

We use special events in jQuery to create the “mouseenter” and “mouseleave” events. In addition to those two events we used it to make the “ready” event and I use it to normalize the “mousewheel” event in the mouse wheel plugin.

A Special Event: “tripleclick”

To illustrate the API I’m going to create a new event type called “tripleclick”. To be fired it will require the user click the element three times. If we were to make this a typical jQuery plugin we would create jQuery.fn.tripleclick. However, I want to be able to take advantage of the bind syntax along with the other benefits like event normalization, data, and namespaces that the jQuery event system provides.

The first thing we need to do is create the special event. Each special event has a setup and a teardown method. The setup method gets called when the event is bound and the teardown method gets called when the event is unbound.

It is important to note that these two methods only get called the first time an event of a particular type is bound/unbound per an element. This is because the jQuery event system actually only binds one handler per an event type per an element and manages the other bound handlers itself. In jQuery 1.3 there is a new special event type called “Special All” that operates for all handlers but has a slightly different behavior. However, that is for another article.

The skeleton of our “tripleclick” special event looks like this.

jQuery.event.special.tripleclick = {
    setup: function(data, namespaces) {
        var elem = this;
    },

    teardown: function(namespaces) {
        var elem = this;
    }
};

Notice that the setup event gets passed the data and namespaces that were used when binding the event. Also that the teardown event gets passed the namespaces that were used when unbinding the event. Although, they are only marginally useful here since this is the data and namespaces associated with the very first event handler bound to a particular element. But you could use the data to configure the event for all the handlers of that type for an element. The scope, or the value of this, for these two methods is the element the event is being bound to.

Behind the scenes we are actually going to utilize the native “click” event to keep track of the number of clicks on an element. We’ll also need a handler that actually does the heavy work of keeping track. I’m going to add a third method called handler to the tripleclick special event. To make the code a little more simple I’m going to track the number of clicks on an element by using the jQuery data API.

The updated tripleclick special event looks like this.

jQuery.event.special.tripleclick = {
    setup: function(data, namespaces) {
        var elem = this, $elem = jQuery(elem);
        $elem.bind('click', jQuery.event.special.tripleclick.handler);
    },

    teardown: function(namespaces) {
        var elem = this, $elem = jQuery(elem);
        $elem.unbind('click', jQuery.event.special.tripleclick.handler);
    },

    handler: function(event) {
        var elem = this, $elem = jQuery(elem), clicks = $elem.data('clicks') || 0;
        clicks += 1;
        if ( clicks === 3 ) {
            clicks = 0;
            // set event type to "tripleclick"
            event.type = "tripleclick";
            // let jQuery handle the triggering of "tripleclick" event handlers
            jQuery.event.handle.apply(this, arguments)
        }
        $elem.data('clicks', clicks);
    }
};

To quickly break down the handler code. First we get the number of clicks via the data API and increment the number by 1. Then we check to see if it has been clicked 3 times. If so, we then need to reset the number of clicks and trigger the other event handlers as the comment indicates. Finally, we store the new value for the number clicks on the element via the data API.

The handler has to set the event type to “tripleclick” because behind the scenes we actually use a click event. jQuery uses the event type to know which handlers it should call and we want it to call the event handlers for our “tripleclick” event.

The Example

We can now use our special event just like we’d use any other event via the bind method. For example to bind a “tripleclick” event to all divs we’d write the following code.

jQuery('div').bind('tripleclick', function(event) {
    alert('triple clicked');
});

You could enhance this event by requiring all three clicks to be within a certain timeframe. You could achieve this by also storing the event.timeStamp property of the previous click and comparing the distance between it and the latest click.

The return Value

I mentioned in the beginning of this article that a special event had the ability to bypass some of the internal jQuery event system. The functionality that can be skipped is the actual binding of the event to the element using the addEventListener or attachEvent methods. This functionality is skipped based on the return value. Any value other than false prevents jQuery from actually binding the event to the element. In other words if you add return false to the setup and teardown methods of your special event, it will actually use the native DOM APIs to bind the event to the element. In the case for our “tripleclick” event we didn’t want to actually bind the event to the element using the native DOM APIs so we didn’t return anything (undefined).