Extending jQuery's Change Listener

Will Holcomb

21 February 2010

The jQuery javascript library allows a programmer to associate a change listener with a html input element and call a function when a field is altered. Consider the following code:

Anytime the text in the element with id "example" is changed, it is copied to mirror.

Useful enough, but it only works for form fields. It would be handy to be able to listen to other sorts of changes in the document as well.

The first extension I would like is the ability to listen to changes to an element's attributes. What if I wanted to have the same mirroring idea from above, but use it for the style attribute. What would that look like? The most obvious analogue is:

$('#example').attr('style').change(function() {
  $('#mirror').attr('style', $(this).attr('style'));
})

This won't work, however, because the attr function returns a value. There are two directions I could see going to work around this. Option one is to add an function that returns an encapsulation of the attribute:

$('#example').prop('style').change(function() {
  $('#mirror').attr('style', $(this).val());
})

The other is to pass in the attribute name as a text string:

$('#example').change('style', function() {
  $('#mirror').attr('style', $(this).attr('style'));
})

The former method seems more in line with jQuery syntax. So I need a function that takes a jQuery object and transforms it to a list of attributes. Only DOM elements can be added to jQuery objects though, so a custom holder has to be created:

There is a change event listener on the base element which also stores a callback list. When an event is fired, it is filtered out to the appropriate listeners.

The resulting application code is pretty close to what was desired:

My goal is to link changes in a data model for the page to the attributes of elements which means listening for changes to objects as well. The application code should look about the same:

var obj = { };
$(obj).prop('style').change(function() {
  $('#mirror').attr(this.name, this.value);
})

The code for this works by wrapping the object access in getters and setters which monitor changes and fire events. At this point the getter and setter code isn't fixed for IE.

My goal with this is to link the values of data structure elements to the value of attributes of DOM nodes. With the change listeners, that code is relatively simple:

The resultant application code seems straightforward:

You may have noticed that both boxes are green, this is because the attribute mirror example linked the styles of the two boxes together, so setting example to green changed mirror as well.