kganser.com

json-table available for jQuery

Json-table is a fully customizable client-side table renderer for displaying json data. At its core, it supports generating a table backed by an array of javascript objects (via optional hooks for generating the table, row, and cell components) and primitive operations on the underlying data (removal, insertion, and sort) that automatically propagate to the DOM.

$(element).table(data, columns, format);

columns is an array of keys representing the columns of the table, which defaults to the set of all keys present in data when omitted. format is an optional object providing one or more of three formatting hooks:

var format = {
  table: function(columns, hooks) {
    // columns is the array of columns provided or generated
    // hooks is this format object
    return [outerNode, innerNode];
  },
  row: function(value, index) {
    // value is the object from data at index
    return [outerNode, innerNode];
  },
  cell: function(key, value, index, row) {
    // index is the column number
    // row is the parent row's data object
    return outerNode;
  }
};

These hooks return one or two DOM nodes: outerNode is attached to the parent component's innerNode. The resulting json-table supports the following methods. All but get are chainable.

Examples

Basic

The most basic example simply generates a table whose columns are the keys of the data array's objects.

var data = [
  {Company: 'Yahoo', CEO: 'Marissa Mayer', Founded: '1994', Headquarters: 'Sunnyvale, CA'},
  {Company: 'Google', CEO: 'Larry Page', Founded: '1998', Headquarters: 'Mountain View, CA'},
  {Company: 'Facebook', CEO: 'Mark Zuckerberg', Founded: '2004', Headquarters: 'Menlo Park, CA'},
  {Company: 'Apple', CEO: 'Tim Cook', Founded: '1976', Headquarters: 'Cupertino, CA'}
];
$('#basic').table(data);

Sorting Headers

This example adds sorting functionality to the table by adding click handlers to its th elements via a custom cell formatter.

var table = $('#sorting').table(data, null, {
  cell: (function(lastSorted, order) {
    // keep track of last sorted header element and order
    return function(value, index, key, row) {
      // row is null for header cells
      var elem = $(row ? '<td>' : '<th>').text(value);
      if (!row) elem.click(function() {
        if (lastSorted) lastSorted.className = '';
        order = this == lastSorted ? -order : 1;
        (lastSorted = this).className = order > 0 ? 'asc' : 'desc';
        table.sort(function(a, b) {
          return order * a[value].localeCompare(b[value]);
        });
      });
      return elem[0];
    };
  })()
});

Spreadsheet

This example uses jsml to generate an editable spreadsheet with fixed columns.

var spreadsheet = $('#spreadsheet').table(data, [''].concat(Object.keys(data[0])), {
  table: function(columns, hooks) {
    var body, head = hooks.row(columns, -1);
    return [$.jsml({table: function(table) {
      return [
        {thead: columns.map(function(column) {
          return {th: column ? column : {className: 'menu', children: [
            {span: {
              className: 'edit',
              title: 'Edit',
              onclick: function() {
                table.className = table.className == 'editing' ? '' : 'editing';
                try { table.getElementsByTagName('input')[0].focus(); } catch (e) {}}}},
            {span: {
              className: 'add',
              title: 'Add Row',
              onclick: function() {
                spreadsheet.insert({}); }}}]}}})},
        {tbody: function(e) { body = e; }}]; }}), body]; },
  cell: function(key, value, index, row) {
    return $.jsml({td: key
      ? [{div: value}, {input: {
          type: 'text',
          value: value || '',
          onkeyup: function(e) {
            row[key] = this.previousSibling.textContent = this.value; }}}]
      : {className: 'menu', children: [
          {span: {
            className: 'reorder',
            title: 'Reorder',
            onmousedown: function(e) {
              var move, up, pos = spreadsheet.get().indexOf(row),
                  style = $('<style>* { cursor: -webkit-grabbing !important; cursor: grabbing !important; }</style>').appendTo('head'),
                  boundaries = Array.prototype.slice.call(this.parentNode.parentNode.parentNode.childNodes, 0, -1).map(function(tr) {
                    return tr.getBoundingClientRect().bottom; });
              document.addEventListener('mousemove', move = function(e) {
                var i = 0;
                boundaries.some(function(y) { return e.clientY < y || !++i; });
                if (pos != i) spreadsheet.remove(pos).insert(row, pos = i);
                e.preventDefault(); });
              document.addEventListener('mouseup', up = function() {
                document.removeEventListener('mousemove', move);
                document.removeEventListener('mouseup', up);
                style.remove(); }); }}},
          {span: {
            className: 'delete',
            title: 'Delete',
            onclick: function() {
              spreadsheet.remove(spreadsheet.get().indexOf(row)); }}}]}}); }});