24 April 2008

Cross-browser data binding with jQuery

From version 4.0 Internet Explorer has had the ability to bind certain HTML elements to a client-side data source. Pretty much any data you can access with ADO you can use as a data source but the one we've used most commonly at work is XML. By adding some attributes to your HTML elements e.g.

<input type="text" datasrc="#dsoComposers" datafld="compsr_last"/>

you can get them to render out the data from the data source and, in the case of inputs, persist any changes back to the source. Additionally, if you have a multi-row data source and you bind it to a table the contents of the tbody tag are duplicated for each row.

This is very useful for making web applications because:

  • you only send the raw data along with a small amount of rendering HTML to the client
  • the client edits the data, adding/deleting rows without postbacks
  • the client posts back structured data rather than a ton of awkwardly named querystring variables
  • you need no presentation code for translating your data to or from HTML

All this considered I thought I'd have a go at creating a simple cross-browser version of this databinding functionality in jQuery with a limited feature set.

Features to implement

For this proof of concept I decided the features I would try and implement were:

  • Support for JSON as a data source
  • Repeat rendering of a table's tbody section for each item in an array of objects
  • CSS class name based way of tying properties of the objects to HTML elements
  • Rendering of property values within HTML elements innerHTML or value
  • Attaching of event handlers to persist data changes on inputs back to the array data source
  • Some ability to reflect programmatic changes to the underlying data source in the bound HTML elements
  • Doing the above without needing to fully refresh the HTML every time

Use

Some "template" HTML...

    <table id="info">
      <thead>
        <tr>
          <th>Common name</th>  
          <th>Origin</th>
          <th>First developed</th>
          <th>Use</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td class="field[name]"></td>
          <td><input type="text" class="field[origin]"/></td>
          <td class="field[developed]"></td>
          <td class="field[use]"></td>
          <td><button class="field[delete]">Delete Row</button></td>
        </tr>
        <tr>
          <td colspan="4" style="border-bottom: 2px solid #888;"><textarea class="field[comments]" style="width: 100%;"></textarea></td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <td colspan="4"><button id="add">Add Item</button></td>
        </tr>
      </tfoot>
    </table>

...some data...

var data = [
   { name: 'Braeburn', origin: 'New Zealand', developed: '1950s, United States', comments: '', use: 'Eating' },
   { name: 'Bramley', origin: 'Southwell, Nottinghamshire, England', developed: 'about 1809', comments: '', use: 'Cooking' },
   { name: 'Cox\'s Orange Pippin', origin: 'Great Britain, New Zealand', developed: 'c. 1829', comments: '', use: 'Eating' },
   { name: 'Empire', origin: 'New York', developed: '1966', comments: 'Lovely white subacid flesh. Tangy taste.', use: 'Eating' },
   { name: 'Granny Smith', origin: 'Australia', developed: '1868, Australia', comments: 'This is the apple once used to represent Apple Records. Also noted as common pie apple.', use: 'Eating or cooking' }
];

...and a small amount of JavaScript...

$(document).ready(function(){
   $('#info').databind(data);
});

The add button handler looks like this...

data.push({ name: '', origin: '', developed: '', comments: '', use: '' });
$('#info').databind(data);

...any code making programmatic changes to the data must recall databind to refresh the bound HTML elements.

Demo

I've set up this quick proof of concept here. I haven't looked at how it performs with large arrays of data but I expect the answer would be badly.

Going forward there may be scope for turning this into a proper jQuery plugin with support for other types of data source and the ability to bind an arbitrary group of elements to some data rather than just a table.

Links

1 comments:

juust said...

You might want to have a look at the ideas of
mark gibson on using the dom mutation event spec to mimick the .dirty form property (I dont know if ms still use it).

Nice post by the way.