VPKR a totally pronounceable jQuery UI widget Download v0.1

VPKR is a filterable, hierarchical data table. It combines the functionality of a data table, an accordion, and an autocomplete.

  • Flexible data source: Accepts inline HTML, JSON, or asynchronous AJAX function
  • Searchability: Typeahead search bar makes sense to users
  • Hierarchy: Accepts a hierarchical data set and displays as expandable trees
  • Versatility: Configure "actions" as functions which operate on data records, add form controls to table rows
  • Simplicity: Small, light, does only the above, doesn't make a mess of the DOM
  • Pronounciation: "Vip-kurr" (or "vee-picker" if you're boring)

Examples

Expandable Tree from Inline HTML

Widget

Apparel Edit
Men's Apparel Edit
Women's Apparel Edit
Footwear Edit
Men's Shoes Edit
Women's Shoes Edit
Decor Edit
Tasteless Edit
Tasteful Edit
Housewares Edit
Tools Edit
Pet Supplies Edit
Iguana Supplies Edit
Cat Supplies Edit
Dog Supplies Edit
Electronics Edit
Yard Maintenance Edit
Auto Care Edit

Markup

<table class="vpkr tree expandable clickToExpand">
  <tr id="vpkr0-node-0001">
    <td class="searchable item-name">
      Apparel
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0001" id="vpkr0-node-0102">
    <td class="searchable item-name">
      Men's Apparel
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0001" id="vpkr0-node-0101">
    <td class="searchable item-name">
      Women's Apparel
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0002">
    <td class="searchable item-name">
      Footwear
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0002" id="vpkr0-node-0202">
    <td class="searchable item-name">
      Men's Shoes
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0002" id="vpkr0-node-0201">
    <td class="searchable item-name">
      Women's Shoes
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0003">
    <td class="searchable item-name">
      Decor
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0003" id="vpkr0-node-0302">
    <td class="searchable item-name">
      Tasteless
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0003" id="vpkr0-node-0301">
    <td class="searchable item-name">
      Tasteful
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0004">
    <td class="searchable item-name">
      Housewares
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0005">
    <td class="searchable item-name">
      Tools
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0006">
    <td class="searchable item-name">
      Pet Supplies
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0006" id="vpkr0-node-0603">
    <td class="searchable item-name">
      Iguana Supplies
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0006" id="vpkr0-node-0602">
    <td class="searchable item-name">
      Cat Supplies
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0006" id="vpkr0-node-0601">
    <td class="searchable item-name">
      Dog Supplies
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0007">
    <td class="searchable item-name">
      Electronics
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0008">
    <td class="searchable item-name">
      Yard Maintenance
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0009">
    <td class="searchable item-name">
      Auto Care
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
</table>

JavaScript


$('#hardcoded .vpkr').vpkr({
  name: "hardcoded",
  actions: {
    "Edit": function(e) {
	alert('Editing ' + $.trim( $(this)
         .closest('tr')
         .children('.item-name')
         .text()) + '!');
    }
  },
  dataSource: 'html'
});
						

AJAX-powered checkable table

Widget

Markup


<table class="vpkr tree checkable">

</table>

JavaScript


$('#dynamic .vpkr').vpkr({
  name: "dynamic",
  dataSource: function(callback){
    $.getJSON('./resources/categories.json',
      function(data){
        callback(data.categories);
    });
  },
  searchText: "Start typing..."
});							

AJAX-powered no-search table with actions

Widget

Markup

<table class="vpkr">

</table>

JavaScript

$('#dynamic2 .vpkr').vpkr({
  name: "dynamic2",
  dataSource: function(callback){
    $.getJSON('./resources/categories.json',function(data){
      callback(data.categories);
    })
  },
  searchBox: false,
  actions: {
    "Edit": function(e,data) {
      var row = $(this);
      row.children('.item-name')
       .html('<input type="text" value="' + data.name + '" />')
       .children('input')
       .bind('keyup.vpkr',function(e){
        if (e.which===13) {
          row.data('vpkr-data',$.extend(row.data('vpkr-data'),{name:this.value}));
          $(this).parent().text(this.value);
        }
      });
    },
    "Flash": function(e,data) {
      $(this).effect('highlight')
    },
    "Delete": function(e,data) {
      $(this).remove();
    }
  }
});							
						

Documentation

At minimum, VPKR needs a table tag.

<table></table>

You can attach a VPKR to this table in one simple command:

$('table').vpkr();

Of course, this table will be empty. If you don't provide VPKR with a data source, it will attempt to populate from the table itself. It will only be successful at this if your HTML follows the VPKR Markup Structure.

Markup Conventions

You can specify boolean options for your VPKR in your HTML, by adding those boolean options as classnames.


<table id="foo" class="tree expandable clickToExpand"></table>
<script type="text/javascript">
  $(function(){
    $("#foo").vpkr({dataSource:someObj});
  })
</script>

is equivalent to


<table id="foo"></table>
<script type="text/javascript">
  $(function(){
    $("#foo").vpkr({tree: true, expandable: true, clickToExpand: true, dataSource:someObj});
  })
</script>

External Data

VPKR is more powerful if you provide it with external data. The data must follow the VPKR JSON Structure, and it can be delivered either synchronously, by setting the dataSource option to the JSON itself, or asynchronously, by setting dataSource to a function that takes a single parameter. The examples above use a jQuery AJAX call to a JSON file.


  dataSource: function(callback){
    $.getJSON('./resources/categories.json',
      function(data){
        callback(data.categories);
    });
  }

Note that the function argued to dataSource must take a "callback" argument; VPKR will send its own callback method to the function, which the function must then execute with the JSON.

It's also possible to update the VPKR with external JSON, using the .vpkr('update') method:


$('#some_table').ajaxComplete(function(e,xhr){
  $(this).vpkr('update',$.parseJSON(xhr.responseText));
})

With no argument, the .vpkr('update') method will reload from the original data source.

Key Type Default Description
actions object (function map) None An optional object map of functions. The keys of this object, e.g. "Edit", will display as text links on each row. The functions will be executed in the context of the <tr /> DOM element, and receive two arguments: the jQuery event object, and, if there was an external data source, the data row associated with that table.
tree boolean false Displays the table data hierarchically, based on the "childOf" value in the data set.
expandable boolean false Creates buttons to expand and collapse subtrees. Requires "tree".
clickToExpand boolean false Makes the row names clickable to expand and collapse subtrees. Requires "tree", "expandable".
initialState string "collapsed" If set to "expanded", expands all trees upon population.
checkable string None Adds form controls to the table rows. Argue "checkbox" or "radio".
name string None For usage as an identifier and in forms, to name your checkboxes.
dataSource function, JSON object None Accepts an external data source. If left blank, will attempt to populate from HTML already inside the table. If JSON, will populate with that JSON. Arguing a function can make table population asynchronous. The function must take a callback parameter and execute that callback with one argument--the data returned from the asynchronous function.
searchBox jQuery object None Optionally accepts an external input element to serve as a search box. If this option is not used, VPKR will create a search box on the top border of the table.
showAllButton jQuery object None Optionally accepts an external element to serve as a "show all" or "clear search" button.
searchText string "Search..." Sets the default prompt in the autogenerated search box.
indent integer 20 Sets the indent, in pixels, for each subtree.
wrap boolean true If true, wraps the initial table in container elements. Set "false" only if the VPKR renders poorly otherwise.
width integer 500 Width of the VPKR table.
height integer 200 Height of the VPKR table (not counting search box).
Name Arguments Description
collapse .vpkr('collapse',$tr) Collapse an expanded row, recursively. Requires a jQuery-wrapped <tr> element.
expand .vpkr('expand',$tr) Expand a collapsed row. Requires a jQuery-wrapped <tr> element.
clearSearch .vpkr('clearSearch') Clear any filter characters from the search box and display all uncollapsed rows.
filter .vpkr('filter',string) Filter the list by a string. The searchbox uses this method.
update .vpkr('update', [json]) Update and repopulate the table. If you pass a parameter, it must be a JSON object. If you pass no parameter, it will update from the original data source.

If you are generating your VPKR using external data, then first of all, good for you. This is the way to do things. Secondly, this external data must conform to an existing format. It must be an an array of data objects, with each object having at minimum an "id", "name", and, if the record has a parent record, a "childOf" property. The "id" and "name" properties must be strings, and the "childOf" property, if present, must contain the ID of the parent record, e.g.:

[
	{
		"id": "0001",
		"name": "Apparel"
	},
	{
		"id": "0101",
		"name": "Women's Apparel",
		"childOf": "0001"
	},
	{
		"id": "0102",
		"name": "Men's Apparel",
		"childOf": "0001"
	},
	{
		"id": "0002",
		"name": "Footwear"
	}, 
	
	(...)
	
}

Note that the JSON structure provides its hierarchy by reference, instead of actually storing data records beneath other records. The HTML structure that VPKR creates reflects this as well. This has show to dramatically increase performance.

If you'll be parsing inline HTML in the table, instead of using an external data source, then first allow me to register my mild disapproval, and then check out the Markup Structure.

Notes:

  • Parse the JSON into an object before delivering to the VPKR. JQuery's AJAX functions will typically do this automatically.
  • The data record can contain other keys. They will not be displayed to the user, but they will be stored and available in the data object received by your action functions.
  • The "name" field can contain HTML that you want rendered into the table. All text is searchable. A neat trick for searching hidden text is to add a <span style="display:none"></span> to the name field.
  • Upcoming versions of VPKR will be able to deal with data in a literal hierarchy, or perhaps even XML.

In order for VPKR to read a table into its data store, your application will have to render HTML that VPKR would have rendered itself. Fortunately, VPKR's HTML is relatively simple!


<table class="vpkr tree expandable clickToExpand">
  <tr id="vpkr0-node-0001">
    <td class="searchable item-name">
      Apparel
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0001" id="vpkr0-node-0102">
    <td class="searchable item-name">
      Men's Apparel
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr class="child-of-vpkr0-node-0001" id="vpkr0-node-0101">
    <td class="searchable item-name">
      Women's Apparel
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
  <tr id="vpkr0-node-0002">
    <td class="searchable item-name">
      Footwear
    </td>
    <td align="right">
      <a href="#" class="vpkr-action-edit">Edit</a>
    </td>
  </tr>
(...)
</table>

First, you must create a unique prefix for all of your table rows. The above example uses

vpkr0-node-. Each table row element must have an ID consisting of the prefix, and then the ID of the data in the row. Table row elements for child nodes (which may come in any order) must have an ID, and also a classname consisting of the prefix "child-of-" and then the prefix, and then the ID of the parent.

Identify certain text nodes as containing searchable text using the "searchable" class. The filter function will only examine these nodes.

Any actions you have configured in your VPKR constructor must have their links hardcoded into this table, as in the example above. The functions you put in the actions array will run when you click those links, even though VPKR did not generate them itself.

Notes:

  • The VPKR table element can contain classnames corresponding to boolean options in the VPKR widget. The presence of these classnames sets these options to true. This allows you to use the same VPKR incantation on multiple tables on the same page with different options, all called with e.g. $('.vpkr').vpkr();. Huzzah!