I\'m working on a fiddly web interface which is mostly built with JavaScript. Its basically one (very) large form with many sections. Each section is built based on options
I was searching for the same thing and hitted your question... none of the answers satisfied my needs so I came up with this solution that I would like to share:
var ObservedObject = function(){
this.customAttribute = 0
this.events = {}
// your code...
}
ObservedObject.prototype.changeAttribute = function(v){
this.customAttribute = v
// your code...
this.dispatchEvent('myEvent')
}
ObservedObject.prototype.addEventListener = function(type, f){
if(!this.events[type]) this.events[type] = []
this.events[type].push({
action: f,
type: type,
target: this
})
}
ObservedObject.prototype.dispatchEvent = function(type){
for(var e = 0; e < this.events[type].length; ++e){
this.events[type][e].action(this.events[type][e])
}
}
ObservedObject.prototype.removeEventListener = function(type, f){
if(this.events[type]) {
for(var e = 0; e < this.events[type].length; ++e){
if(this.events[type][e].action == f)
this.events[type].splice(e, 1)
}
}
}
var myObj = new ObservedObject()
myObj.addEventListener('myEvent', function(e){// your code...})
It's a simplification of the DOM Events API and works just fine! Here is a more complete example
Small modification to the previous answer : by moving the observable code to an object, one can make an abstraction out of it and use it to extend other objects with jQuery's extend method.
ObservableProperties = {
events : {},
on : function(type, f)
{
if(!this.events[type]) this.events[type] = [];
this.events[type].push({
action: f,
type: type,
target: this
});
},
trigger : function(type)
{
if (this.events[type]!==undefined)
{
for(var e = 0, imax = this.events[type].length ; e < imax ; ++e)
{
this.events[type][e].action(this.events[type][e]);
}
}
},
removeEventListener : function(type, f)
{
if(this.events[type])
{
for(var e = 0, imax = this.events[type].length ; e < imax ; ++e)
{
if(this.events[type][e].action == f)
this.events[type].splice(e, 1);
}
}
}
};
Object.freeze(ObservableProperties);
var SomeBusinessObject = function (){
self = $.extend(true,{},ObservableProperties);
self.someAttr = 1000
self.someMethod = function(){
// some code
}
return self;
}
See the fiddle : https://jsfiddle.net/v2mcwpw7/3/
Mozilla-engined browsers support Object.watch, but I'm not aware of a cross-browser compatible equivalent.
Have you profiled the page with Firebug to get an idea of exactly what's causing the slowness, or is "lots of event handlers" a guess?
Thanks for the comments guys. I've gone with the following:
var EntriesRegistry = (function(){
var instance = null;
function __constructor() {
var
self = this,
observations = {};
this.set = function(n,v)
{
self[n] = v;
if( observations[n] )
for( var i=0; i < observations[n].length; i++ )
observations[n][i].apply(null, [v, n]);
}
this.get = function(n)
{
return self[n];
}
this.observe = function(n,f)
{
if(observations[n] == undefined)
observations[n] = [];
observations[n].push(f);
}
}
return new function(){
this.getInstance = function(){
if (instance == null)
{
instance = new __constructor();
instance.constructor = null;
}
return instance;
}
}
})();
var entries = EntriesRegistry.getInstance();
var test = function(v){ alert(v); };
entries.set('bob', 'meh');
entries.get('bob');
entries.observe('seth', test);
entries.set('seth', 'dave');
Taking on-board your comments, I'll be using event delegation on the form objects to update the registry and trigger the registered observing methods.
This is working well for me so far... can you guys see any problems with this?
jQuery is just amazing. Although you could take a look to ASP.NET AJAX Preview.
Some features are just .js files, no dependency with .NET. May be you could find usefull the observer pattern implementation.
var o = { foo: "Change this string" };
Sys.Observer.observe(o);
o.add_propertyChanged(function(sender, args) {
var name = args.get_propertyName();
alert("Property '" + name + "' was changed to '" + sender[name] + "'.");
});
o.setValue("foo", "New string value.");
Also, Client Side templates are ready to use for some interesting scenarios.
A final note, this is fully compatible with jQuery (not problem with $)
Links: Home page, Version I currently use
You could attach a listener to a container (the body or the form) and then use the event parameter to react to the change. You get all the listener goodness but only have to attach one for the container instead of one for every element.
$('body').change(function(event){
/* do whatever you want with event.target here */
console.debug(event.target); /* assuming firebug */
});
The event.target holds the element that was clicked on.
SitePoint has a nice explanation here of event delegation:
JavaScript event delegation is a simple technique by which you add a single event handler to a parent element in order to avoid having to add event handlers to multiple child elements.