in jQuery

Trigger Custom Events with jQuery

Things have come a long way since the early days of the web and standard JavaScript window.onload and element.onclick event handling. Even using JavaScript on your web page was risky business in the early days because the availability and acceptance of JavaScript was so unpredictable.

JavaScript frameworks and libraries like jQuery have brought a level of ease and sophistication to web applications that is hard to overstate. One of the truly wonderful features of jQuery is the ability to respond to any user event by binding event handlers to just about anything – event custom events that you define.

This JavaScript magic is accomplished using the jQuery.bind() and jQuery.trigger() methods. The jQuery.bind() method attaches a bit of code to an event which will be executed later when jQuery.trigger() is called on that event.

jQuery.bind() – A Closer Look

There three ways to bind callbacks to events in jQuery. The first uses the standard jQuery.bind() method:

$("#some-element").bind("click", function(/* jQuery.Event */ event) {
    doSomethingKickAss(event);
});

jQuery also provides a shorthand method for the most common events:

$("#some-element").click(function(event) {
    doSomethingKickAss(event);
});

In jQuery parlance, this is the same as doing the following in traditional, vanilla JavaScript:

document.getElementById("some-element").onclick = function(event) {
    event = event || window.event;
    doSomethingKickAss(event);
};

The function that you attach to the event is referred to as a callback or event handler. An instance of a jQuery.Event object is passed to the function as the first argument by default.

$("#some-element").bind("click", function(event) {
    doSomethingKickAss(event);
});

The jQuery example above is probably familiar to you but there is a lot more you can do with jQuery.bind().

Binding Event Handlers to Multiple Events

You can attach a callback to multiple events at once by providing the event names as space-separated items:

$("#some-element").bind("mouseenter mouseleave", function(event) {
    doSomeKickAssHovering(event);
});

Or you can pass your events and callbacks as a JavaScript object:

$("#some-element").bind({
    "mouseenter": function(/* jQuery.Event */ event) {
        // Do something kick-ass
    },
    "mouseleave": function(/* jQuery.Event */ event) {
        // Do something else kick-ass
    },
    "click": function(/* jQuery.Event */ event) {
        // Do something even more kick-ass
    }
});

Custom jQuery Events

In addition to the standard DOM Events such as click, mouseenter and mouseleave, jQuery allows you to attach callbacks to custom events on elements. Let’s give our custom event the very creative name of main_event. We can bind a callback to the event with the code below. Take note of the second parameter called params. It will be explained shortly.

$("#some-element").bind("main_event", function(event, params) {
    doSomethingKickAss(params);
});

Now at some later point in our code, we can trigger the event thus:

$("#some-element").trigger("main_event", "Known at execution time");

Another thing to note about custom events is that they do not have to be attached to a DOM element. Events can be attached to any object. For instance, if you create a custom JavaScript object, you can bind callbacks to custom events to the object.

/*
  Edit: the next line does not work in some
  browsers but I'm leaving it here so the comments
  on the blog post make sense.
*/
// if (! MyObject) MyObject = {};
// Deleted 3/18/2013: if (typeof(MyObject) != "undefined") MyObject = {}
// Thanks, lonhosford & Erik Phipps. Good catch.
if (typeof(MyObject) === "undefined") MyObject = {};

/**
 * Bind a callback to a custom event on MyObject
 */
$(MyObject).bind("main", function(event, theData) {
    alert(theData);
});

/**
 * Now trigger the custom event on the custom object
 */
$(MyObject).trigger("main", "Welcome to the main event");

The technique above becomes especially useful when you are creating a heavy-weight, custom application with jQuery such as the WYMeditor HTML Editor. I will explore this technique in detail in an upcoming post on pluggable jQuery applications.

Passing Data to Event Handlers in jQuery

There are two ways to pass data to an event handler: at the time the event handler is bound or at the time the event handler is triggered. Let’s tackle the second method first.

Notice the second argument in the previous example – the string that read Some data known at execution time. When jQuery executes the callback that was attached to #some-element, the string will be passed in the params parameter. The parameter can contain any data type you need it to: a string, an array, an object or any other JavaScript data type.

You can also pass data to the event handler when it is bound to an event by passing the data as part of the jQuery.Event object. You do it like this:

var theData = "Known before execution time";
$("#some-element").bind("click", theData, function(event) {
    doSomethingKickAss(event.data);
});

Notice that the data is passed as the second parameter of the jQuery.bind() method. Also notice that you access the passed data via the jQuery.Event.data property.

Conclusion

In this article, I have explained how to use jQuery.bind() and jQuery.trigger(). Using custom events and these two methods in jQuery allows you to build sophisticated rich applications that can respond to users’ actions in a very fine-tuned manner. In the next post, I will demonstrate a real-world use of custom events and explain their use can help improve code management.

Leave a Reply

15 Comments

  1. This is very nice, but say you have 40 elements bound to the same event and you want to trigger them all without overhead. example:

    You have 40 of these:

    $(‘#myElement1’).on(‘myEvent’, ‘myElement’, function () {});
    $(‘#myElement2’).on(‘myEvent’, ‘myElement’, function () {});
    $(‘.myClass’).on(‘myEvent’, ‘myElement’, function () {});

    How would you go about firing myEvent on these different selectors? $(‘body *’) will fire them on everything and is a major performance issue…

    • Jan, that’s a very good question and I honestly don’t know the answer. But now I’m intrigued enough to try to find an answer. It seems that not only might performance be an issues but with that many elements & events, there might also be the danger of a collision between them. What if one event callback alters an element in such a way that a subsequent event handler is nullified?

      • Yes, this was a huge problem. I found a usable solution. I have a global variable for each event type where I add the selector. This way I have the selectors in a cached string and can use that string within the $(cached_selector_string_here).trigger(eventName, [data]).

        When I define the handlers I use the new .on() method to delegate the events like so $(document).on(‘eventName’, ‘selector’, function () {});

        And just before that I add the ‘selector’ part to the cached_selector_string_here. Just remember to have one cached_selector_string_here for each event you make up.

        This way I keep control of the selectors that are bound to the specific event. And can fire them only on the elements that I want them to fire on. If I inside the function also uses stopPropagation() they won’t bubble either(performance gain?).

        It would be better if jQuery had the possibility of using $.event.trigger instead and that jQuery had internal caching of the selectors but that is not possible in my jquery 1.9 version.

        For my solution it gives me the possibility to fire off the ajax-queries first, bind the .on() handlers outside of the $(document).ready()-function. Bind one handler to each element within the DOM(static or dynamically added) without a huge performance loss or loss of readability.

        The only downside is a small maintenance overhead…

        • Have you performed any metrics on your approach? If you can demonstrate the performance gains, this might be really useful information to a lot of other developers. You should write up an explanation of the problem, your solution, and your metrics. Thanks for sharing it here at any rate.

          • I will see if I can fix up a blog post on it. I think my solution can interest alot of developers struggling with performance because they are using alot of $(document).on() definitions and needs to trigger maybe 10-15 of these in one operation. The lazy one uses $(‘body *’).trigger() to fire these events and realizes that “whooops, that is a very bad idea!'”…

          • These functions was what I came up with as a solution.

            var EventSelectors = new Array();

            function setAndReturnSelector(event, selector) {
            var events = event.split(‘ ‘);
            for (var i = 0; i < events.length; i++) {
            var eventName = events[i];
            if (!EventSelectors[eventName]) {
            EventSelectors.push(eventName);
            EventSelectors[eventName] = new Array();
            }
            EventSelectors[eventName].push(selector);
            }
            return selector;
            }
            function triggerEvent(event, data) {
            for (var index in EventSelectors[event]) {
            var selector = EventSelectors[event][index];
            $(selector).trigger(event, data);
            }
            }

            You use them as such $(document).on(eventName, setAndReturnSelector(eventName, selector), funcToRun(event, data){});

            When you want to trigger the events you call triggerEvent(eventName, data);

            The setAndReturnSelector function keeps track of all the selectors and runs events on them all when you call the triggerEvent… That way you have very little extra code to write to keep track of all your selectors.

            Cheers!

            PS: Sorry about the formatting, I have no idea how to make it better…

          • I think that would be helpful. The jQuery site does warn against using too many $(document) bindings but I don’t think it offers any alternate approaches. I look forward to seeing a writeup of your solution. If you have a blog of your own send me the link and I’ll be happy to link to it. If you don’t have a blog I’ll be happy to post your solution here. This blog makes absolutely no money so don’t feel like I’d be profiting off of your content. It would be solely for the benefit of anyone searching for a solution. This blog gets decent but not great traffic (250 visitors per day). I wish I had a lot more time to devote to this blog but I have so many projects going on all the time that this one gets the least attention. I punch code all day so usually in my free time I prefer to work on my design-oriented projects.

    • For what it’s worth, I found this article by Jon Raasch which covers some tips by Paul Irish for improving the performance of jQuery event handlers. It doesn’t directly address your question about having a large number of handlers attached to an event but the suggestions could improve overall performance.

      http://bit.ly/1wNnUG