Practical .NET

ASP.NET: Managing DOM Events Interoperably

Peter Vogel introduces you to the new dynamic event model for JavaScript that's available in all the contemporary browsers.

While all the buzz is around the new elements in HTML5 (and when they will be supported in the various models), the new DOM event model hasn't gotten as much attention, which is too bad because it's implemented and ready to use in all the contemporary browsers. The new model provides a flexible way to attach events to objects/elements in the page while cleanly separating the code for wiring up events from HTML. The new model also makes it very easy to define, fire and catch your own custom events.

In this new world, you don't "fire" or "raise" events—you "dispatch" them. To process events you add a listener to an element. When an event is dispatched it propagates up through the tree of elements in the page, triggering any listeners on the elements it passes through.

For this example, I'll use a simple example of a table and a button, nested within a div element (which, in turn, is nested within the DOM's document and window objects):

 <div id="MyDiv">
<table id="MyTable">
..table definition
</table>
<input id="MyButton" type="button" value="Post Data">
</div>

In real life, I might want to capture events for individual cells or rows in the table but, for clarity's sake, I'll just assume that I want to capture the click event that's fired when the user clicks anywhere in the table.

Processing Default Events

Since the table will, all by itself, fire a click event, my first step is to tie an event listener to the table element to listen for click events. The jQuery code to find the element with an id attribute of MyTable and tie a function called processTable to an event called 'click' looks like this (I'll come back to the third parameter being passed to processTable shortly):

  $("#MyTable").each(function () {
this.addEventListener("click", processTable, false);
});

Of course, I could have tied a click event to every row by using $("tr") as my jQuery selector.

The processTable function that, in this example, will be called when the click event for the table is dispatched can accept a single parameter (that parameter is automatically provided by the DOM event model). My function would look like this:

 function processTable(e) {
…process table click event…
}

The parameter that's passed contains a wealth of information. Its target property, for instance, gives you access to the element that fired the event.

However, the event doesn't stop moving up through the elements in the page just because a listener processes it. This means I could have tied my click event listener to the div element and still process the click event fired by the table. That listener would also catch the click event fired by the button within the div element. That design would make sense if the processing for the two events was similar (and, if there were differences, I could check the e.Target.id property to determine whether it was the button or the table that had fired the click event).

You can manage that propagation process in two ways. First, when you add an event listener, you can specify that this listener is to get the event before any other listener by passing true in the addEventListener's third parameter. This code, for instance, adds an event listener to the document but specifies that all click events from any element in the page are to go to this listener first before continuing up through the normal propagation path:

document.addEventListener("click", helloDiv, true);

Within an event listener, I can prevent an event from propagating up the tree by calling the stopPropagation method on the parameter passed to the function (this method's name may change to stopImmediatePropagation when the specification is finalized—IE 9 supports both names). This example prevents any subsequent listener seeing the event:

 function processTable(e) {
…process table click event…
e.stopPropagation();
}
Dispatching Your Own Events

If you want to dispatch your own events from within your functions, first define your event using the document object's createEvent method. When you create an event, you specify the event model you want to use. This controls, for instance, what properties will appear on the parameter passed to the function that handles the event. There are several libraries including one dedicated to user interface event (UIEvents). In this example, I'm using the standard Event library:

 var e =  document.createEvent("Event");

After you've created an event, you need to give it a name and specify whether it can bubble up or be cancelled. In this example, I'm specifying that the event won't bubble but can be cancelled:

 e.initEvent("MyEvent", false,  true);

Adding a listener for this event would look like this:

 window.addEventListener("MyEvent", processMyEvent, false);

Finally, to dispatch this event from within your code, you call the dispatchEvent on any element:       

.dispatchEvent(evt);

These are the key functions in the new event model (there's more, of course). What's most attractive to me about the model is how consistent it is among the contemporary browsers -- very good news for Web developers.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • ML.NET Improves Object Detection

    Microsoft improved the object detection capabilities of its ML.NET machine learning framework for .NET developers, adding the ability to train custom models with Model Builder in Visual Studio.

  • More Improvements for VS Code's New Python Language Server

    Microsoft announced more improvements for the new Python language server for Visual Studio Code, Pylance, specializing in rich type information.

  • Death of the Dev Machine?

    Here's a takeaway from this week's Ignite 2020 event: An advanced Azure cloud portends the death of the traditional, high-powered dev machine packed with computing, memory and storage components.

  • COVID-19 Is Ignite 2020's Elephant in the Room: 'Frankly, It Sucks'

    As in all things of our new reality, there was no escaping the drastic changes in routine caused by the COVID-19 pandemic during Microsoft's big Ignite 2020 developer/IT pro conference, this week shifted to an online-only event after drawing tens of thousands of in-person attendees in years past.

  • Visual Studio 2019 v16.8 Preview Update Adds Codespaces

    To coincide with the Microsoft Ignite 2020 IT pro/developer event, the Visual Studio dev team shipped a new update, Visual Studio 2019 v16.8 Preview 3.1, with the main attraction being support for cloud-hosted Codespaces, now in a limited beta.

Upcoming Events