JavaScript event handling is the basis of all client-side applications. When an event occurs on a target element, e.g. a button click, mouse move, form submit etc, a handler function is executed. An event object is passed to the handler which provides various properties and a number of methods to prevent default actions.
One drawback is that events are inextricably linked to DOM elements. Consider a simple form which accepts messages from the user:
<form id="msgbox" action="#" method="get">
<label for="msg">your message</label>
<input id="msg" value="" />
<button>SEND</button>
</form>
We can write a handler to echo messages to the screen when that form is submitted, e.g.
document.getElementById("msgbox").addEventListener("submit", function(e) {
e.preventDefault();
var msg = e.currentTarget.getElementById("msg").value.trim();
if (msg) {
alert(msg);
}
}, false);
What if we also wanted to send the message as a tweet, store it on a server, or perform other actions? We have two options with existing event delegation methods:
- Add further code to our existing handler.
This is inflexible since we need to update and test our handler function every time we append, change or remove functionality. There could be dozens of uses for the posted message and we’re trying to apply them all within the same block of code. - Create further event handlers for each use.
This would result in more elegant code but leads to maintenance issues. First, every function must execute similar actions to extract and validate the message. And what if we need to change our form? Simply renaming the ID would require us to change the event handling code for every subscriber.
Wouldn’t it be great if we could simply raise a custom “newMessage” event whenever a valid message is posted? It would be even better if we could simply monitor the document or body
tag rather than referencing a specific form
node. That’s exactly what custom events permit us to do.
Raising a custom event is simple; we pass the name, details and options to a new CustomEvent object:
var event = new CustomEvent(
"newMessage",
{
detail: {
message: "Hello World!",
time: new Date(),
},
bubbles: true,
cancelable: true
}
);
In this example, “newMessage” is the custom event type. The second parameter is an object with three properties:
- detail: a child object providing custom information about the event. In this example, we’ve added a message and time.
- bubbles: if true, events will bubble to ancestors of the element which fired the event.
- cancelable: if true, events can be canceled using the event object’s stopPropagation() method.
Now, we need to dispatch this event on a specific element, e.g.
document.getElementById("msgbox").dispatchEvent(event);
Any number of handlers can subscribe to this event using code such as:
document.addEventListener("newMessage", newMessageHandler, false);
Demonstration Page
This example demonstrates the technique:
View the Custom Events demonstration page
A standard event handler looks for submissions on the HTML form above. The function gets the current message and, assuming it’s valid, dispatches a new “newMessage” event.
var msgbox = document.getElementById("msgbox");
msgbox.addEventListener("submit", SendMessage, false);
// new message: raise newMessage event
function SendMessage(e) {
e.preventDefault();
var msg = document.getElementById("msg").value.trim();
if (msg && window.CustomEvent) {
var event = new CustomEvent("newMessage", {
detail: {
message: msg,
time: new Date(),
},
bubbles: true,
cancelable: true
});
e.currentTarget.dispatchEvent(event);
}
}
Handlers can now subscribe to “newMessage” events. The events are only raised if there’s a valid message and, since bubbles is set to true, the event can be applied to the form or any of it’s ancestors such as the root document, e.g.
// listen for newMessage event
document.addEventListener("newMessage", newMessageHandler, false);
// newMessage event handler
function newMessageHandler(e) {
LogEvent(
"Event subscriber on "+e.currentTarget.nodeName+", "
+e.detail.time.toLocaleString()+": "+e.detail.message
);
}
The message itself can be extracted from the detail.message property of the event object.
Browser Compatibility
At the time of writing, the CustomEvent object is supported by Chrome, Firefox and Opera. It’s available in nightly editions of Safari so it’s likely to arrive in that browser soon.
IE9 and below do not support the object. Fortunately, several JavaScript libraries support custom event delegation, so keep watching SitePoint for a cross-browser solution soon.
And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like Jump Start JavaScript.
Comments on this article are closed. Have a question about JavaScript? Why not ask it on our forums?