JavaScript Fu: Event Observer Delegation

In JavaScript, events (clicks, focus, change, etc.) are all said to “bubble”, as in bubble up. If you click on a link, that click is felt first on the link, but then ripples outward like a stone in a pond, hitting the parent P of that link, then the div that contains it, and so on, up to the document.

If you want to have one particular link trigger some effect, you might set an observer on it:

$('your_link_id').observe('click',function(evt){ do_something(); });

Which is fine for one or three, but gets hugely old after a short while, especially if you’re changing things later.

Back to the bubble, then. Using the bubbling effect, you can set a single observer on the page or another high-level container object, and get the effect of individual observers on each and every child in that container for free. And you can use class names to add behavior to elements, so you could have multiple effects fire within a single function. Best of all, this technique is very lazy. Lazy in that you don’t have to wire up a lot of different observers, but also lazy in that it doesn’t do much of anything until a click happens. There’s very little wasted effort until the user actually tries to do something, and then only the path that is taken gets activated. It’s very kind to low-memory browsers like the iPhone.

Here’s a snip from the phone numbers trick I was expounding on earlier:

//first, we set all this inside a document observer
document.observe('dom:loaded',function(){
	//if the container is there
	if($('container')){
		//define an anonymous function
		var fx = function(evt){
			//this is the heart of the trick: element() determines
			//which object first received the event
			var elm = evt.element();
			//now we just test to see if it's the one we want
			if(elm.hasClassName('remove_phone')){
				if(!elm.previous('select').disabled){
					if(elm.previous('input').getValue() == ''){
						//if there's no content, just hide 
						//and return instead of saving for undo
						elm.previous('input').hide();
						elm.previous('select').hide();
						return elm.hide();
					}
					elm.previous('input').disable();
					elm.previous('select').disable();
					elm.src = 'Resources/icns/arrow_undo.png';
				}else{
					elm.previous('input').enable();
					elm.previous('select').enable();
					elm.src = 'Resources/icns/delete.png';
				}
			}
		};
		//the end of the function, now we set it to observe a click
		$('container').observe('click',fx);
	}
});

This function defines a reusable behavior that is triggered when a click is registered on the outer container, and the click is determined to have originated on an object with the class name remove_phone. We don’t have to know how many phone numbers are inside that container beforehand; in fact, we can add new ones on the fly without defining a new listener for each one as it’s created, or removing a listener when its parent object is destroyed.

Let the bubbles go to your head!

Walter


dynamo mailing list
email@hidden
Update your subscriptions at:
http://freewaytalk.net/person/options