HTML5 Development Center

Developed for you in part by
 
693-javascript-custom-events

How to Create Custom Events in JavaScript

By | | HTML | HTML5 | JavaScript

6

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:

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

The Ultimate JavaScript Bundle: 2 books + 1 course

Buy now $39 Normally $117 - save 66%

Or get access to all SitePoint's Premium Content with a Learnable membership

Craig Buckler

Craig is a Director of OptimalWorks, a UK consultancy dedicated to building award-winning websites implementing standards, accessibility, SEO, and best-practice techniques.

More Posts - Website

{ 6 comments }

Les July 12, 2012 at 3:14 am

I do like you personally as you’ve contributed greatly to Sitepoint over the years however I would have enjoyed the article more if you had provided examples in the context of using JQuery, as lets face it, it’s the defacto standard we’re all used to using now!

Great attempt but I was expecting more :)

Jason Knight July 10, 2012 at 6:13 pm

Seems kinda silly when you could just add a method to the object and call it instead of wasting time on pointless event handler overhead.

Craig Buckler July 11, 2012 at 1:07 am

The reasons are explained at the top. Events tied to DOM elements may not always be practical, especially if your HTML structure is likely to change.

Wojciech Fornal July 10, 2012 at 11:36 am

You can declare input element this way:
input name=”msg” value=”"
and address it in JS this way:
var msg = e.currentTarget.msg.value.trim();

Wojciech Fornal July 10, 2012 at 11:32 am

Line:
var msg = e.currentTarget.getElementById(“msg”).value.trim();
produces error:
‘e.currentTarget.getElementById’ is not a function…

e.currentTarget points to an object of type ‘HTMLFormElement’ (which is basically pure HTML containing tags and everything in between) and the code mentioned above tries to find element named ‘getElementById’ inside the form

to fix that, just add name attribute to the input element (you can get rid of id), for example:

and change:
var msg = e.currentTarget.getElementById(“msg”).value.trim();
to:
var msg = e.currentTarget.msg.value.trim();

It’s nice feature of JavaScript that we can use element names and dot notation to address nested elements.

Craig Buckler July 11, 2012 at 1:04 am

Thanks Wojciech.

Which browser failed? It should work fine in all latest browsers except IE which doesn’t support custom events.

Comments on this entry are closed.