views:

681

answers:

5

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 from other parts of the form. Whenever those options change the new values are noted in a "registry" type object and the other sections re-populate accordingly.

Having event listeners on the many form fields is starting to slow things down, and refreshing the whole form for each change would be too heavy/slow for the user.

I'm wondering whether its possible to add listeners to the registry object's attributes rather than the form elements to speed things up a bit? And, if so, could you provide/point me to some sample code?

Further information:

  • This is a plug-in for jQuery, so any functionality I can build-on from that library would be helpful but not essential.
  • Our users are using IE6/7, Safari and FF2/3, so if it is possible but only for "modern" browsers I'll have to find a different solution.
+1  A: 

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.

Ken
A: 

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?

insin
+4  A: 

As far as I know, there are no events fired on Object attribute changes (edit: except, apparently, for Object.watch).

Why not use event delegation wherever possible? That is, events on the form rather than on individual form elements, capturing events as they bubble up?

For instance (my jQuery is rusty, forgive me for using Prototype instead, but I'm sure you'll be able to adapt it easily):

$(form).observe('change', function(e) {
    // To identify changed field, in Proto use e.element()
    // but I think in jQuery it's e.target (as it should be)
});

You can also capture input and keyup and paste events if you want it to fire on text fields before they lose focus. My solution for this is usually:

  1. Gecko/Webkit-based browsers: observe input on the form.
  2. Also in Webkit-based browsers: observe keyup and paste events on textareas (they do not fire input on textareas for some reason).
  3. IE: observe keyup and paste on the form
  4. Observe change on the form (this fires on selects).
  5. For keyup and paste events, compare a field's current value against its default (what its value was when the page was loaded) by comparing a text field's value to its defaultValue

Edit: Here's example code I developed for preventing unmodified form submission and the like:

http://stackoverflow.com/questions/194101/what-is-the-best-way-to-track-changes-in-a-form-via-javascript#194347

eyelidlessness
A: 

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

Brian J Cardiff
A: 

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?

digitala