Click here to Skip to main content
15,115,787 members
Articles / Web Development / HTML
Article
Posted 25 Jan 2015

Tagged as

Stats

35.3K views
816 downloads
16 bookmarked

JSON Object to HTML Table in Pure JavaScript

Rate me:
Please Sign up or sign in to vote.
4.82/5 (6 votes)
25 Jan 2015CPOL8 min read
Universal function for filling html table with JSON objects

Table of Contents

Introduction

Task of filling table with JSON objects seems to be trivial. Indeed, it is in cases when object’s structure (set of keys) is known.
But what if we want function to be universal and be able to handle "table row" objects with unknown upfront structure? That alone will require a little bit more effort from the developer.
Let’s go even further. What if we want to have universal function that can also do the following:

  • Accepts input JSON in its string representation or in (parsed) object form
  • Accepts input JSON in different formats, in particular, as array of "table row" objects or as object with such array as key’s value
  • Fills existing table or creates, fills and returns the new one
  • Adds rows to various existing table’s sections or replaces section's existing content
  • Considers most of the input parameters as optional

Here, we will build exactly that kind of function that hopefully will be the only function we need whenever task of filling html table with JSON object is encountered.

Input JSON Data Format Definition/Requirement

Central element of input data is JSON object ("table row" object), which with its key/value pairs forms table columns. We target filling of one table. Therefore, we will handle one set (array) of "table row" objects. Since JSON is built on two structures, we want our universal function to handle both of them.
Requirements for the input JSON data are:

  • Structurally JSON input is expected to be in one of two formats:
    • Array of "table row" objects
    • Object (name/value pair) where value of (first) key is array of "table row" objects
  • All "table row" objects have same set of keys though not necessary in the same order

Input Parameters and Return Value

In order to add versatility to the function, let's create input parameters list that presumable covers most practical tasks of filling HTML table with JSON data.
Complete list of parameters in order of how they appear in function's call and their explanation is provided below. All parameters except aJSON are optional and have default values. Optional parameters must form a group at the end of parameter list. In other words, if, for example, aTable parameter is missing, then all parameters after it must be missing too.

  • aJSON (required parameter)

String representation or parsed JSON object in one of the following structures:

  • Array of "table row" objects
  • Object with first (others are ignored) key-value pair where value is array of "table row" objects
  • Note that "table row" objects must have identical set of keys.
    • aAddHead
  • false (by default) - does not change content of the head
  • true  - forms "one row" head with "table row" JSON object keys replacing existing head content if any
    • aTable
  • null (by default) - function will create and return table object with single body section filled and possible head (see aAddHead above)
  • Existing table object - will fill that table in the way controlled by other input parameters
    • aClearSectionToFill
  • false (by default) - rows will be added to the end of the table's section being filled.
  • true  - before filling, all existing rows will be removed from the section
  • This parameter is ignored if new table is created
    • aSectionToFill
  • null (by default) - will fill last tBody section creating one if none exists
  • Particular TableSectionObj - will fill that table section. Usually (one of the) body but theoretically can be foot or head.
  • This parameter is ignored if new table is created (new single body will be filled)
    • aClearEntireTable
  • false (by default)
  • true  - before filling table, removes rows from all table's sections without removing sections
  • This parameter is ignored if new table is created
    • aClearHead
  • false (by default)
  • true - removes all rows (if any) from tHead section without removing section
  • This parameter is ignored if aClearEntireTable == true or aAddHead == true or new table is created
    • aClearAllBodies
  • false (by default)
  • true - removes all rows (if any) from all tBody sections without removing sections
  • This parameter is ignored if aClearEntireTable == true or new table is created
    • aClearFoot
  • false (by default)
  • true - removes all rows (if any) from tFoot section without removing section
  • This parameter is ignored if aClearEntireTable == true or new table is created
    • Return Value
  • If filling existing table, then function returns same table object that was passed as input parameter aTable
  • If creating and filling new table, then function returns newly created table object.

Code Explained

Handling Input Parameters

First of all, let's make sure required parameter is specified.

JavaScript
if (!Boolean(aJSON))
  throw "jsonObjToHtmlTable: Required parameter aJSON is not specified";

We will translate input parameters into the set of local variables that will actually be used to control the process. Translation logic also provides default values for optional parameters.
First two are obvious:

JavaScript
var addHead = Boolean(aAddHead);
var createNewTable = !Boolean(aTable);

Next group is related to clearing table or its sections. Obviously, we do not need to clear newly created table. Also, we will not need code that explicitly handles clearing of entire table because we consider that operation as "sum" of "clear head", "clear all bodies" and "clear foot".

JavaScript
var clearSectionToFill = !createNewTable && Boolean(aClearSectionToFill);
var clearTable = !createNewTable && Boolean(aClearEntireTable);
var clearHead = !createNewTable && (clearTable || addHead || Boolean(aClearHead));
var clearAllBodies = !createNewTable && (clearTable || Boolean(aClearAllBodies));
var clearFoot = !createNewTable && (clearTable || Boolean(aClearFoot));

Last group is related to table object and its section elements. Some of them can get null values and those cases will be handled down the road correspondingly.

JavaScript
var tblToFill = createNewTable ? document.createElement("table") : aTable;
var tblHead = tblToFill.tHead;
var tblFoot = tblToFill.tFoot;
var tblBodies = tblToFill.tBodies;
var sectionToFill = createNewTable ? null : aSectionToFill;

We would like to figure out which exactly table's section rows must be added to. If at this point sectionToFill is not specified, then by default, we will use the last available body section creating one if it does not exist.

JavaScript
if (!sectionToFill) {
  if (!(Boolean(tblBodies) && (tblBodies.length > 0))) {
    tblToFill.appendChild(document.createElement("TBODY"));
    tblBodies = tblToFill.tBodies;
  }
  sectionToFill = tblBodies[tblBodies.length - 1];
}

Avoiding Nested Functions

We need the ability to clear (delete rows of) various table sections. A straightforward solution would be nested function usage. However there is a suggestion to avoid nested functions in JavaScript since often that can harm performance and/or memory usage. Even though our case seems not to be one of the "harmful" ones, we still will follow that suggestion but using other solution. Let's define that sub-function as a method of our jsonObjToHtmlTable function object:

JavaScript
if (!jsonObjToHtmlTable.clearTableSection)
 jsonObjToHtmlTable.clearTableSection = 
                      function (aTblSection) {
                        for (var i = aTblSection.rows.length - 1; i >= 0; i--)
                          aTblSection.deleteRow(i);
                      };

Clearing Table or its Sections

Before actual table filling, we need to clear table or its particular sections if it was requested. All possible kinds of clearing were defined on input parameter interpretation stage. Now, if some particular kind of clearing is requested and corresponding table's section exists, then we will clear it.

JavaScript
if (clearHead && Boolean(tblHead))
  jsonObjToHtmlTable.clearTableSection(tblHead);
if (clearAllBodies && Boolean(tblBodies) && (tblBodies.length > 0))
  for (var i = tblBodies.length - 1; i >= 0; i--)
    jsonObjToHtmlTable.clearTableSection(tblBodies[i]);
if (clearFoot && Boolean(tblFoot))
  jsonObjToHtmlTable.clearTableSection(tblFoot);
if (clearSectionToFill)
  jsonObjToHtmlTable.clearTableSection(sectionToFill);

Handling String or Object Input

We want our function to be universal and accept input object of different types. Since we are dealing with JSON here, we can think of two possibilities for the input object, specifically, object comes in text/string format or as already parsed JavaScript object. Therefore, let's check whether input is a string and parse it or use it as is otherwise:

JavaScript
var  inputJSObj = (Object.prototype.toString.call(aJSON) === "[object String]") ?
                     JSON.parse(aJSON) : aJSON;

Using Object.keys() Method

As it was already pointed out, we want to handle "table row" objects with unknown upfront structure, i.e., unknown number of properties/keys and their names. But obviously, we still need to have access to property/key values. Object.keys(obj) method, which returns an array of a given object's property/key names, can be helpful here. Considering that not all browsers support this method, for instance Internet Explorer before ver 9, let's handle such cases defining method by ourselves if necessary:

JavaScript
if(!Object.hasOwnProperty("keys")){
  Object.keys = function(aObj){
                  var keyNameArray = [];
                  for (keyName in aObj){
                    if (aObj.hasOwnProperty(keyName)){
                      keyNameArray[keyNameArray.length] = keyName;
                    }
                  }
                  return keyNameArray;
                };
}

Handling Array or key/value Input

The next step is to figure out the structure of input object. Since according to previously formulated requirement, input can be of format of array or "key/value" pair, let's just check whether it is array and use it as is or extract value of (first) key, which should be array too.

JavaScript
var  allRowsAsArrayOfObjects = (Object.prototype.toString.call(inputJSObj) === "[object Array]") ?
                                 inputJSObj : inputJSObj[Object.keys(inputJSObj)[0]];

At this point, we have array of objects that will form table's rows. We will proceed with the rest of the steps only if this array is not empty (allRowsAsArrayOfObjects.length > 0).
Since we require all "table row" objects to have the same key set, we can get the set from any object, in particular, the first one:

JavaScript
var rowKeyNames = Object.keys(allRowsAsArrayOfObjects[0]);

Forming Table's Head Section

If table's head forming is requested and tHead section exists, then it should be already cleared earlier (see logic for clearHead). If tHead does not exist, then we will create one. After that, we just need to add row to the head and fill its cells with values from above rowKeyNames array. Just one inconvenience, since tHead.insertCell() method creates <td> cells and we want <th> cells for the head, we need to use little bit longer code:

JavaScript
var headRow, headRowCell;
if (addHead) {
  tblHead = tblHead ? tblHead : tblToFill.createTHead();
  headRow = tblHead.insertRow(-1);
  for (var j = 0; j < rowKeyNames.length; j++) {
    headRowCell = document.createElement("th");
    headRowCell.appendChild(document.createTextNode(rowKeyNames[j]));
    headRow.appendChild(headRowCell);
  }
}

Adding Data Rows

Finally, we are ready to actually fill the table with data rows. In the previous stages, it was determined which exactly table section is supposed to be filled and section was cleared if necessary. The rest is simple. For each "table row" object, add row to the section creating necessary row’s cells and assigning their values. In order to use a uniform approach, we will create <td> cells in the same way we create <th> cells for table's head though we could use insertCell() here.

JavaScript
var oneRowAsObject, tableRow, rowCell;
for (var i = 0; i < allRowsAsArrayOfObjects.length; i++) {
  oneRowAsObject = allRowsAsArrayOfObjects[i];
  tableRow = sectionToFill.insertRow(-1);
  for (var j = 0; j < rowKeyNames.length; j++) {
    rowCell = document.createElement("td");
    rowCell.appendChild(document.createTextNode(oneRowAsObject[rowKeyNames[j]]));
    tableRow.appendChild(rowCell);
  }
}

Complete Code of the Function

Complete code, that includes all discussed above pieces, is provided below:

JavaScript
/* Copyright (c) 2015 Vladimir Nikitenko - Code Project Open License (CPOL) */

function jsonObjToHtmlTable(aJSON, aAddHead, aTable, aClearSectionToFill, aSectionToFill,
                            aClearEntireTable, aClearHead, aClearAllBodies, aClearFoot) {
  if (!Boolean(aJSON))
    throw "jsonObjToHtmlTable: Required parameter aJSON is not specified";
  var addHead = Boolean(aAddHead);
  var createNewTable = !Boolean(aTable);
  var clearSectionToFill = !createNewTable && Boolean(aClearSectionToFill);
  var clearTable = !createNewTable && Boolean(aClearEntireTable);
  var clearHead = !createNewTable && (clearTable || addHead || Boolean(aClearHead));
  var clearAllBodies = !createNewTable && (clearTable || Boolean(aClearAllBodies));
  var clearFoot = !createNewTable && (clearTable || Boolean(aClearFoot));

  var tblToFill = createNewTable ? document.createElement("table") : aTable;
  var tblHead = tblToFill.tHead;
  var tblFoot = tblToFill.tFoot;
  var tblBodies = tblToFill.tBodies;
  var sectionToFill = createNewTable ? null : aSectionToFill;
  if (!sectionToFill) {
    if (!(Boolean(tblBodies) && (tblBodies.length > 0))) {
      tblToFill.appendChild(document.createElement("TBODY"));
      tblBodies = tblToFill.tBodies;
    }
    sectionToFill = tblBodies[tblBodies.length - 1];
  }

  if (!jsonObjToHtmlTable.clearTableSection)
    jsonObjToHtmlTable.clearTableSection = 
                         function (aTblSection) {
                           for (var i = aTblSection.rows.length - 1; i >= 0; i--)
                             aTblSection.deleteRow(i);
                         };
  if (clearHead && Boolean(tblHead))
    jsonObjToHtmlTable.clearTableSection(tblHead);
  if (clearAllBodies && Boolean(tblBodies) && (tblBodies.length > 0))
    for (var i = tblBodies.length - 1; i >= 0; i--)
      jsonObjToHtmlTable.clearTableSection(tblBodies[i]);
  if (clearFoot && Boolean(tblFoot))
    jsonObjToHtmlTable.clearTableSection(tblFoot);
  if (clearSectionToFill)
    jsonObjToHtmlTable.clearTableSection(sectionToFill);

  var inputJSObj, allRowsAsArrayOfObjects, oneRowAsObject, rowKeyNames;
  var headRow, headRowCell, tableRow, rowCell;

  if(!Object.hasOwnProperty("keys")){
    Object.keys = function(aObj){
                    var keyNameArray = [];
                    for (keyName in aObj){
                      if (aObj.hasOwnProperty(keyName)){
                        keyNameArray[keyNameArray.length] = keyName;
                      }
                    }
                    return keyNameArray;
                  };
  }
  inputJSObj = (Object.prototype.toString.call(aJSON) === "[object String]") ? 
                 JSON.parse(aJSON) : aJSON;
  allRowsAsArrayOfObjects = (Object.prototype.toString.call(inputJSObj) === "[object Array]") ?
                              inputJSObj : inputJSObj[Object.keys(inputJSObj)[0]];
  rowKeyNames = [];
  if (allRowsAsArrayOfObjects.length > 0){
    rowKeyNames = Object.keys(allRowsAsArrayOfObjects[0]);
    if (addHead) {
      tblHead = tblHead ? tblHead : tblToFill.createTHead();
      headRow = tblHead.insertRow(-1);
      for (var j = 0; j < rowKeyNames.length; j++) {
        headRowCell = document.createElement("th");
        headRowCell.appendChild(document.createTextNode(rowKeyNames[j]));
        headRow.appendChild(headRowCell);
      }
    }
    for (var i = 0; i < allRowsAsArrayOfObjects.length; i++) {
      oneRowAsObject = allRowsAsArrayOfObjects[i];
      tableRow = sectionToFill.insertRow(-1);
      for (var j = 0; j < rowKeyNames.length; j++) {
        rowCell = document.createElement("td");
        rowCell.appendChild(document.createTextNode(oneRowAsObject[rowKeyNames[j]]));
        tableRow.appendChild(rowCell);
      }
    }
  }
  return tblToFill;
}

Downloading Source Code

The following source code, all of which is in one zip file, is available for download at the top of this page:

  • Code of JSON object to HTML function in jsonObjToHtmlTable.js file.
  • Test/Demo application in jsonObjToHtmlTable_Test.html file. This application, which is in fact html page, allows demonstration and testing of all features of the function. It references the above external file containing tested function assuming that it is located in the same directory.

History

Version 1.0 (2015-01-25)

  • First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Vladimir Nikitenko
Systems Engineer
United States United States
Extensive experience developing pure software and combined soft-hardware systems using variety of languages and tools.

Comments and Discussions

 
GeneralMy vote of 3 Pin
Member 1382734514-May-18 20:21
MemberMember 1382734514-May-18 20:21 
QuestionSyntax Error :Unexpected Token V Pin
venkateshpappu27-Jan-15 0:25
Membervenkateshpappu27-Jan-15 0:25 
AnswerRe: Syntax Error :Unexpected Token V Pin
Vladimir Nikitenko27-Jan-15 7:29
MemberVladimir Nikitenko27-Jan-15 7:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.