Let’s say you are working on a web application in jQuery and you are tasked with writing an accordion suited to the needs of your web app. You decide to make it into a plugin. That way anywhere in this app that needs an accordion, they can load this plugin and call it upon semantic markup that is ready to become an accordion. Then ta-da, they have a lovely working accordion.
Now another developer is using your accordion and they want to be able to Ajax load some content into one of the sections of the accordion, but only when that section is opened. Together you decide that you should keep your code separate. The generic functionality of how an accordion works should be separate from code specific to one accordion on one page of an app.
Callbacks, the traditional way
One way to approach that would be to write “callbacks” into your plugin. The other developer would specify the name of a function that she wants run when certain actions happen on that accordion. So the calling of it might be like:
$(".accordion").accordion({
panelOpen: myPanelOpenCallback,
panelClose: myPanelCloseCallback;
});
And they would have created those callback functions themselves:
function myPanelOpenCallback() {
// Do Ajax stuff
}
Then the plugin itself would honor those callbacks in its related internal functions:
$.fn.accordion = function(options) {
return this.each(function(i, el) {
var base = el;
base.init = function() {
// Do initialization stuff
};
base.openPanel = function(panel) {
// Open panel
// Do callback
options.panelOpen.call();
};
base.closePanel = function(panel) {
// Open panel
// Do callback
options.panelClose.call();
};
base.init();
});
};
Custom Events, a better way
Now let’s say a third developer gets involved, and they also want to write a bit of JavaScript that reacts to a panel opening as well. For the sake of a use case, say what’s inside the accordion panels are settings, and this developer wants to save those settings whenever keys are opened or closed automatically.
Now we’re in an interesting position. We’ve already defined our callback for accordion panels opening and closing. So in the callback model, these developers are going to have to get together on that callback and run both of their code in that one callback. Not that big of a deal, but now we’re forced to mix code when that might not be ideal. Remember we started this whole thing off by separating specific functionalities.
Here’s where custom events are super rad. Instead of firing off a specified callback, the accordion functionality plugin fires off a custom event. Custom events are just like any other event (e.g. click) only they are only declared and called programmatically.
First, callbacks are gone, we just call the accordion plugin without them:
$(".accordion").accordion();
Now in place of where we called the callbacks, we’ll trigger our Custom Event. Basically, just make up a name that makes sense (like you are naming a function).
// OUT
// options.panelOpen.call();
// IN
panel.trigger("panelOpen");
Now our other developers can bind their stuff to this custom event and do their own thing.
$(".panel").on("panelOpen", function() {
// Developer 1: do Ajax stuff
});
// Meanwhile, in another part of town...
$(".panel").on("panelOpen", function() {
// Developer 2: do saving stuff
});
Yay for freedom! Yay for separation of functionality!
In my opinion custom events are just better all around. I do think they require a bit more communication though. You’ll probably have to write up some comments somewhere that explain what custom events are fired and when and make that easy to discover.
View Demo of Custom Events model
More Information
The scenario outlined above is very simple. The following two articles go way more into depth about Custom Events including more complex examples and more information on how they work.
- Demystifying Custom Events in jQuery by Rebecca Murphey
- jQuery Special Events by Ben Alman
Even better would be to namespace your events.
Where the “open” event is namespaced under “panel.”
Nice article Chris.
Custom events absolute rock.
One small note would be that sometimes you’re better off using .triggerHandler() over just using .trigger to stop the event from bubbling up the dom tree.
For example if you write an event based form validator you can create a custom event titled ‘validate’ and attach it to the form element as well as all the fields inside the form.
If you were now to call .trigger(‘validate’) on the field the form element would also be triggered. If you are using .triggerHandler(‘validate’) on the field, only the field would be triggered, not the form that contains it.
It’s also worth noting that both .trigger and .triggerHandler accept an extra array of options, sort of like the arguments a .call() function can take.
Anyway, good to see people embracing custom events over callback functions!
I like using both methods. Providing a callback on creation would just use custom events behind the scenes. That partially solves the need for extra documentation.
I think qTip2 has a particularly good implementation of custom events if you need to see a real world example. In particular, the use of the event object. Using that provides more flexibility. For example:
For documentation on jQuery’s event object, check out http://api.jquery.com/category/events/event-object/
I use custom events a lot, I had written a post on this : http://blog.imaginea.com/implementing-event-delegation-and-custom-events-in-javascript-using-jquery-prototypejs/
These remind me of eventListeners with the exception that you can’t tell who is listing to what. I find that a bit weird.
Let’s say, 2 buttons both broadcast a ‘press’ event when they get pressed.
One button needs to change the backgound-color of the body while the other needs to change the div height.
If you can’t specify that the body needs to listen to only one button it will react no matter which button is pressed. Same goes for the div of course.
jQuery is very useful JS script library. Using it helped me a lot in development .
Actually, I disagree. This approach encourages APIs which lie about their dependencies.
What do I mean by that? A developer wanders onto a site using your
.accordion
plugin. This is what he sees:Without looking at the source code for that plugin, or some sort of documentation, the developer can’t ever know that the plugin actually fires the said events. However, with the callback approach:
Here, the plugin explicitly declared what it needed in order to operate. The API is truthful, it doesn’t lie by saying “I don’t have any functions that can go with me, I am just an accordion”, it now says “I am an accordion, I support a
whenOpened
andwhenClosed
callback functions, that are getting called when these events happen“I agree with this. Chris, I’d be interested to hear – do you think the custom events method would create more spaghetti-esque code? By that, I mean – in your own file (not in the plugin), have lots of seemingly random custom event listeners ( using .on() ), without them being in a structure that is related to the plugin (the callback approach)? I can absolutely see the advantage you brought up in your article (and thanks for the info, btw – super rad), but I think if this method is used a lot throughout a project, the code would start to become very difficult to manage.
Thanks!
While I agree with you somewhat, the fact that you mention API and documentation, I would expect a decent plugin to list its API in document form, therefore I don’t see this as too big a problem.
I guess we can use a combination of both. Jquery plugins have a tradtion of using callbacks, so I’m inclined to keep it this way.
The event call after the plugin initialization can be overlooked, while callbacks in options won’t.
A quick implementation : http://jsbin.com/usaboz/2
i love that
thnx chris ::D
A brilliant article, custom events do give us developers so much more freedom. Thanks for sharing this information, i’ll be forwarding it onto a few friends who I know will also enjoy reading this.
A quick message to Chris regarding this comment form of yours. While I love the look and layout of this comment form, I’m not too fond of the icons you’ve used to explain what each textbox is for. I had to THINK about what goes where… I worked out the name and email icons okay but I struggled to guess the third website URL icon. I know your a big believer in your blogs accesability so I thought I should point this small issue out to you as I’m sure others have had the same problem. Even if you just added a very simple hover popup for the icons that would solve the problem. Personally speaking, I’d change the third url icon to a chain/link icon as everyone would understand that.
I hope you don’t mind me posting this here, I know its not related to this article specifically so after you’ve read this I don’t mind if you delete it. Also, just incase you do have a hover discription on each textbox and its actually my own browser thats at fault, for your referrence I’m using Firefox version 3.6.
If you used a modern web browser, you’d see the preview text in the text fields. If you were visually impaired, you’d hear the descriptive text associated with the fields. I think it’s time for you to update.
If you used a modern web browser, you’d see the preview text in the text fields. If you were visually impaired, you’d hear the descriptive text associated with the fields. I think it’s time for you to update.
—–
^ that’s a rude and churlish answer to a valid user interface complaint, especially since Firefox 3.6 IS a valid browser, and not everyone is in a situation where they can just “upgrade”
The text disappears when the field has focus, so, unless you memorized what text was there before, you won’t know what to type there and the ‘icon’ doesnt help one bit.
Not a big deal, since that field isnt required, but why bother??
jQuery UI’s widget factory supports triggering events as part of the widget. It also allows for binding the events as part of the initialization object.
These are totally awesome! I’m playing with custom events now.
Custom events and modular, extensible code are all pretty awesome things…I just wish people would realize they are just now becoming aware of features and principles that have been present in MooTools for more than 6 years – this.fireEvent(‘customEventName’). If you want even more flexible, extensible code that has all the features mentioned in the article (and much more) I encourage you to give MooTools a try, it is simply beyond jQuery’s limited DOM-centered scope in so many ways (I recently built a whole node.js framework with it because it rocks on the server side as well!)
Just wanted to say: Merry Christmas to all and a Happy New Year.
What?
Chris! I liked your method – when you hover the title, the read more link also is hovered. Can you write me how is it made? thanks in advance
I’m a bit confused. This will only work if the component is programmed for raising the custom events, right?
Thanks.