How many times have you come across the following kind of code in JavaScript
// code in script-1.js
// eek, two global variables!
var dialog,
updateMessage = function updateMessage (message) {
dialog.text(message);
}
// function to run when the DOM has loaded
$(function () {
// #dialog is just a simple <p> element
dialog = $('#dialog');
});
then in another script file
// code in script-2.js
// function to run when the DOM has loaded
$(function () {
$('select').change(function () {
// update the dialog with the new selection
updateMessage('the new value is ' + this.value);
});
});
All we’re doing here is updating the text of a <p> element in the DOM whenever the value of a <select> element changes. It’s a contrived and trivial example but it perfectly demonstrates the tight coupling between these two script files; if the updateMessage
function is renamed, removed or modified to reorder the parameters (let’s put aside the use of arguments for a moment), this is going to break script-2 at runtime. What’s more, usually with these kinds of changes, it’s too easy to forget to search through all script and markup-related files for function usages and modify accordingly. And further still, it's polluted the global namespace with two additional variables (a big no no in JavaScript development), dialog
and updateMessage
, to allow script-2 access to the updateMessage function (which needs access to the dialog
variable). There has to be a better way, and there is.
Custom Events
Custom events have been in jQuery core for as long as I can remember, at least back as far as 1.2.6 in 2008. They are similar to an event bus for sending messages (read: objects) when something happens on the client side and subscribing to knowing when that something happens. Let’s look at a revised version of the previous code, using jQuery custom events
// code in script-1.js
// function to run when the DOM has loaded
$(function () {
// #dialog is just a simple <p> element
var dialog = $('#dialog');
// bind a handler to the custom namespaced event "dialog.updateMessage"
$(document).on('dialog.updateMessage', function (e, message) {
dialog.text(message);
});
});
and now in script 2,
// code in script-2.js
// function to run when the DOM has loaded
$(function () {
$('select').change(function () {
// raise the dialog.updateMessage and pass it a message
$(document).triggerHandler('dialog.updateMessage', ['the new value is ' + this.value]);
});
});
In script 1, we set up an event handler function on document to listen for the namespaced event ‘dialog.updateMessage’
. This takes the event object, e
, and an additional parameter, message
, which it uses to update the text of dialog. In script 2, we set up an event handler on <select> elements so that whenever the change event is raised, the ‘dialog.updateMessage’
event is triggered, passing in a string that includes the new values as the first item in an array of additional data arguments. The end result? See for yourselves (hit the play icon on the right of the jsfiddle window):