DOM Events in JavaScript

By  on  

This API has been deprecated -- please instead use the MutationObserver API!

Mozilla Firefox 7 introduced CSS' useful text-overflow: ellipsis, an outstanding method of dynamically and elegantly concatenating strings within their parent elements. Firefox was late to the text-overflow party, so the Dojo Toolkit offered dojox.ellipsis, a resource that would shim ellipsis functionality with an iFrame. When perusing the dojox.ellipsis code, I found that the creators used the DOMSubtreeModified event to recheck all nodes when the page structure changes. After a bit of research, I found a whole host of DOM events you can use to spy on the document.

The DOM Tree Events

Here are the DOM tree events you can listen for:

Event name Spec Description
DOMActivate W3C Draft A user agent must dispatch this event when a button, link, or other state-changing element is activated. Refer to Activation triggers and behavior for more details. (Deprecated in favor of click)
DOMAttrModified W3C Draft A user agent must dispatch this event after an Attr.value has been modified and after an Attr node has been added to or removed from an Element.
DOMAttributeNameChanged W3C Draft A user agent must dispatch this event after the namespaceURI and/or the nodeName of a Attr node have been modified (e.g., the attribute was renamed using Document.renameNode()).
DOMCharacterDataModified W3C Draft A user agent must dispatch this event after CharacterData.data or ProcessingInstruction.data have been modified, but the node itself has not been inserted or deleted.
DOMContentLoaded HTML5
DOMElementNameChanged W3C Draft A user agent must dispatch this event after the namespaceURI and/or the nodeName of an Element node have been modified (e.g., the element was renamed using Document.renameNode()).
DOMFocusIn W3C Draft A user agent must dispatch this event when an event target receives focus. The focus must be given to the element before the dispatch of this event type. This event type must be dispatched after the event type focus.
DOMFocusOut W3C Draft A user agent must dispatch this event when an event target loses focus. The focus must be taken from the element before the dispatch of this event type. This event type must be dispatched after the event type blur.
DOMNodeInserted W3C Draft A user agent must dispatch this event type when a node other than an Attr node has been added as a child of another node. A user agent may dispatch this event when an Attr node has been added to an Element node (see note below). This event must be dispatched after the insertion has taken place.
DOMNodeInsertedIntoDocument W3C Draft A user agent must dispatch this event when a node has been inserted into a document, either through direct insertion of the node or insertion of a subtree in which it is contained; a user agent may optionally treat an Attr node as part of an Element's subtree. This event must be dispatched after the insertion has taken place.
DOMNodeRemoved W3C Draft A user agent must dispatch this event when a node other than an Attr node is being removed from its parent node. A user agent may dispatch this event when an Attr node is being removed from its ownerElement (see note below). This event must be dispatched before the removal takes place.
DOMNodeRemovedFromDocument W3C Draft A user agent must dispatch this event when a node is being removed from a document, either through direct removal of the node or removal of a subtree in which it is contained; a user agent may optionally treat an Attr node as part of an Element's subtree. This event must be dispatched before the removal takes place.
DOMSubtreeModified W3C Draft This is a general event for notification of all changes to the document. It can be used instead of the more specific mutation and mutation name events. It may be dispatched after a single modification to the document or, at the implementation's discretion, after multiple changes have occurred. The latter case should generally be used to accommodate multiple changes which occur either simultaneously or in rapid succession. The target of this event must be the lowest common parent of the changes which have taken place. This event must be dispatched after any other events caused by the mutation(s) have occurred.

Table provided by MDN documentation.

The DOM event you'll probably recognize is DOMContentLoaded, which is used to signal the domready we're all used to seeing within our JavaScript toolkits. The event names are all fairly self-explanatory. Adding these events is as easy as adding any other type of event:

// Notify us when any node within the document is modified, added removed, etc.
document.addEventListener("DOMSubtreeModified", function(e) {
	// Notify of change!
	console.warn("change!", e);
}, false);

// Now create a new element to see what it will look like
var a = document.createElement("a");
document.body.appendChild(a);

/*
	Result:

	{
		ADDITION: 2,
		MODIFICATION: 1,
		REMOVAL: 3,
		attrChange: 0,
		attrName: "", 
		defaultPrevented: false,
		newValue: "",
		prevValue: "",
		relatedNode: null,
		initMutationEvent: initMutationEvent(),
		bubbles: true,
		cancelable: false,
		constructor: MutationEvent { MODIFICATION=1, ADDITION=2, REMOVAL=3},
		currentTarget: Document en,
		eventPhase: 3,
		explicitOriginalTarget: body.home,
		isTrusted: true,
		originalTarget: body.home,
		target: body.home,
		timeStamp: 0,
		type: "DOMSubtreeModified"
	}

*/

What if you want to listen to simple node changes?

// Listen to when an image src or alt gets changed (ex: slideshow, etc.)
document.getElementById("slideshowImage").addEventListener("DOMAttrModified", function(e) {
	// Record the occurrence
	console.warn(e.attrName + " changed from ", e.prevValue," to: ", e.newValue);
}, false);

The attrName, prevValue, and newValue values tell you which attribute changed and its previoius and current values. Each event type has its own custom event properties so experiment with events when creating them. You can also learn the event properties by checking out the spec list.

These DOM events are really nice to plug into if you're looking for complete control over Document. The one caution to throw to you is that since these events can fire so often, attaching to them can be heavy on your app. The ability to attach to them when needed is incredibly useful in a sophisticated web application.

Recent Features

Incredible Demos

  • By
    Check All/None Checkboxes Using MooTools

    There's nothing worse than having to click every checkbox in a list. Why not allow users to click one item and every checkbox becomes checked? Here's how to do just that with MooTools 1.2. The XHTML Note the image with the ucuc ID -- that...

  • By
    jQuery Countdown Plugin

    You've probably been to sites like RapidShare and MegaUpload that allow you to download files but make you wait a specified number of seconds before giving you the download link. I've created a similar script but my script allows you to animate the CSS font-size...

Discussion

  1. Wow, not only an eye opener to me, but a nice reference as well. I should browse MDN more, probably.

    Thanks for the article ;-)

  2. These events are called “mutation events”. Unfortunately, they have been deprecated in the W3C specs, and I think they’re in the process of being removed from Firefox. I remember reading that they made the page way too slow (browsers need to disable a lot of their optimisations). Some replacements have been proposed (http://www.w3.org/2008/webapps/wiki/MutationReplacement) but for now they’re quite unreliable.

    See http://lists.w3.org/Archives/Public/www-dom/2009AprJun/0072.html for example:
    “Firefox, for example, when it realizes that a mutation event has been turned on, instantly goes into an incredibly-slow code path where it has to fire events at every single DOM modification. This means that doing something like .innerHTML = “foo” where it wipes out 1000 elements would fire, at least 1000 + 1 events (1000 removal events, 1 addition event).”

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!