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