How to Create Custom Events in JavaScript

JavaScript event handling is the basis of all client-side applications. When an event occurs on a target element, eg 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, eg:

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:

  1. 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.
  2. 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, eg:

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, eg:

// 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.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.brothercake.com/ James Edwards

    How is this any different than using a single delegated event listener that’s filtered by criteria for which elements it responds to? I mean, you the custom event syntax doesn’t let you define the specific interaction that fires the event, so you still have to bind it to an existing event to make something happen with it, so then where is the benefit of defining a custom event at all?

    Or have I misunderstood?

    • http://www.optimalworks.net/ Craig Buckler

      A custom event need not be triggered by DOM event — I’m using one in the example, but it could be fired by the result of an Ajax call or some other condition.

      However, it may still useful even when a DOM event is occurring. In essence, you’re creating an event proxy which any handler can subscribe to. That means you only need to change code in one place if, say, the DOM node changed or you wanted to process data prior to dispatch.

  • http://www.brothercake.com/ James Edwards

    Gotcha :-)