June 3rd, 2007

In order to create javascript objects which encapsulate all of their logic and create a direct bridge to the HTML DOM event model without helper functions, you can attach events using function closures which, when executed, are executed on a specific instance of an instantiated object. I touched on this same approach when discussing how to use setTimeout() for object specific methods.

Let’s have a look at some example code. The following basically will associate the onClick() event of the AlertFoo object with the click event of an HTML element.


function AlertFoo(id) {
	this.htmlRoot = document.getElementById(id);
	this.onClick = function(e){
		alert(this.htmlRoot.getAttribute("id"));
	};
	var _self = this;
	normAddEvent(this.htmlRoot, "click", function(e){
		_self.onClick(e);
	});
}

function load() {
	var alertFoo1 = new AlertFoo("alertId1");
	var alertFoo2 = new AlertFoo("alertId2");
}

function normAddEvent(el, evName, evFx) {
	if (el.addEventListener) {
		el.addEventListener (evName, evFx, false);
	}
	else if (el.attachEvent) {
		var modifiedName = "on" + evName;
		el.attachEvent (modifiedName, evFx);
	}
	else {
		//bleh
	}
}

What we want to create is a wrapper object that takes an HTML element and adds functionality to it through the HTML DOM event model. What we don’t want is to have any code outside of the class (besides derivative extensions) to perform the functionality. That means, no functions just hanging around explicitly calling events on wrapper objects. Therefore, when DOM events fire, in this case the click event, the event of the particular instance of the object that wraps the target HTML element of the event should fire.

The first thing to do is declare AlertFoo.onClick(e). In this case, this event will simply issue an alert with the id name of the element wrapped by this instance of AlertFoo.


function AlertFoo(id) {
	this.htmlRoot = document.getElementById(id);
	this.onClick = function(e){
		alert(this.htmlRoot.getAttribute(”id”));
	};
	var _self = this;
	normAddEvent(this.htmlRoot, “click”, function(e){
		_self.onClick(e);
	});
}

Next, declare the local variable _self and set it to be a reference to this instance.


function AlertFoo(id) {
	this.htmlRoot = document.getElementById(id);
	this.onClick = function(e){
		alert(this.htmlRoot.getAttribute("id"));
	};
	var _self = this;
	normAddEvent(this.htmlRoot, “click”, function(e){
		_self.onClick(e);
	});
}

Finally, declare and attach a function to the click event. Note, I’m using a function called normAddEvent() to do this, just for cross-browser support.


function AlertFoo(id) {
	this.htmlRoot = document.getElementById(id);
	this.onClick = function(e){
		alert(this.htmlRoot.getAttribute("id"));
	};
	var _self = this;
	normAddEvent(this.htmlRoot, “click”, function(e){
		_self.onClick(e);
	});
}

The key to this is _self.onClick(el). What will happen when the code is run is that when the click HTML event fires it will execute the function that was attached, which, because _self is defined in the scope of the function to be an instance of an object, _self.onClick(e) will be the equivalent to this:


var alertFoo1 = new AlertFoo("alertId1");
alertFoo1.onClick(e);
}

I’ve whipped up a quick example of this in action.

Leave a Reply