Creating HTML Tables with JavaScript

In this post I will demonstrate three ways of creating and populating an HTML table with JavaScript, examining the advantages and disadvantages of each.

The table in the screenshot below lists all 118 elements including their names, atomic numbers (the number of protons in their nucleii) and their chemical symbols (the abbreviations used in chemical formulae such as H2O or NaCl).

The table itself as well as its caption, column headings and rows are created dynamically from a data structure, in this case an array of objects, using JavaScript.

Compared to other HTML elements tables are relatively complex and can be fiddly to create so it is worth spending some effort doing so in a way which is both efficient to run and easy to code.

As I mentioned above I am using three different methods to create a table. These are:

  • Building the table as a single string of HTML which is then added to the document as a single operation

  • Using standard DOM manipulation methods, namely createElement, createTextNode and appendChild

  • Using table-specific methods, namely createCaption, insertRow and insertCell

I will discuss each in detail in the code descriptions below.

The Project

The project consists of the following files plus css files and graphics. They can be downloaded as a zip, or you can clone/download the Github repository if you prefer.

  • creatingtables.htm

  • creatingtables.js

  • elements.js

Source Code Links

ZIP File
GitHub

The Data Source

Before getting stuck into creating tables I need to briefly describe the data source. In the real world you would probably be populating a table from, for example, a data structure parsed from JSON obtained from a REST API. For this demo I have hard-coded the data into a static method of a class in elements.js which is part of the download zip and Github repository. The class also has methods to get column headings and object property names.

This is the beginning of the element data just so you can see what it looks like.

elements.js (partial)

class Elements
{
    static GetHeadings()
    {
        return ["Name", "Atomic Number", "Chemical Symbol"];
    }

    static GetProperties()
    {
        return ["name", "atomicnumber", "symbol"];
    }

    static GetElements()
    {
        return [
        {
            name: "Hydrogen",
            atomicnumber: 1,
            symbol: "H"
        },
        {
            name: "Helium",
            atomicnumber: 2,
            symbol: "He"
        },
.
.
.
.

Now let's start looking at the code in creatingtables.js.

creatingtables.js part 1: the boring window.onload thing

window.onload = function()
{
    createTable1();

    // createTable2();

    // createTable3();
}

Here we call one of the three functions to create and populate our table. You can of course comment/uncomment the function calls to run the one you want.

Below is the first of these functions.

creatingtables.js part 2: building a string of HTML

function createTable1()
{
    const elements = Elements.GetElements();
    const headings = Elements.GetHeadings();
    const properties = Elements.GetProperties();

    // start table and add caption
    let tablehtml = "<table><caption>Elements</caption>";

    // insert row of headings
    tablehtml  += "<tr>";
    for(let heading of headings)
    {
        tablehtml  += `<th>${heading}</th>`;
    }
    tablehtml += "</tr>";

    // iterate data and add row of cells for each
    for(let element of elements)
    {
        tablehtml  += "<tr>";

        for(let property of properties)
        {
            tablehtml  += `<td>${element[property]}</td>`;
        }

        tablehtml  += "</tr>";
    }

    // end of table
    tablehtml += "</table>";

    // add table to the empty div
    document.getElementById("tablediv").innerHTML = tablehtml;
}

The overall structure of the three functions is the same:

  • Grab the data
  • Create a table
  • Add a caption
  • Add column headings
  • Insert the data
  • Insert the table into the empty div in creatinghtmltables.htm

In this first one we create a variable called tablehtml to hold the table as a string, and it is initialized with the opening table tag and caption.

Next we add a row and iterate the headings, adding a cell for each, before closing the row tag.

After that we iterate the array of elements, adding a row for each and then iterating the properties of each element object, adding cells as we go along.

Finally we just need to close the table and set it as the innerHTML of the empty div.

The advantage of this approach is that we are only hitting the DOM once at the end when setting innerHTML. Of course all the elements still need to be added to the DOM but this is done at a lower level by the JavaScript engine rather than our JS function calls. In the second function below we are calling DOM methods several hundred times, even for this fairly modestly sized data set.

The disadvantage is that we are hard-coding HTML into JavaScript (cough JSX cough *). It's pretty difficult to avoid this completely but extensive HTML in JavaScript is fiddly to write and maintain and violates any separation of concerns dogma you subscribe to.

* I once got into a spat with someone about hard-coding big chunks of HTML in JSX. His view was that it is not HTML, it is JSX. Mine was that if it walks like HTML and quacks like HTML it's HTML.

Let's move on to standard DOM methods.

creatingtables.js part 3: standard DOM methods

function createTable2()
{
    const elements = Elements.GetElements();
    const headings = Elements.GetHeadings();
    const properties = Elements.GetProperties();

    // create table
    const table = document.createElement("table");

    // create caption and add to table
    const caption = document.createElement("caption");
    const captiontext = document.createTextNode("Elements");
    caption.appendChild(captiontext);
    table.appendChild(caption);

    // create row for headings...
    const hrow = document.createElement("tr");
    table.appendChild(hrow);

    // ...and add cells to it
    for(let heading of headings)
    {
        const th = document.createElement("th");
        const thtext = document.createTextNode(heading);
        th.appendChild(thtext);
        hrow.appendChild(th);
    }

    // iterate data, adding rows and cells for each item
    for(let element of elements)
    {
        const drow = document.createElement("tr");
        table.appendChild(drow);

        for(let property of properties)
        {
            const td = document.createElement("td");
            const tdtext = document.createTextNode(element[property]);
            td.appendChild(tdtext);
            drow.appendChild(td);
        }
    }

    // add table to div
    document.getElementById("tablediv").appendChild(table);
}

The overall structure is pretty much the same here, but this function uses createElement and createTextNode for the table, caption, rows and cells, which are then added to their respective parents with appendChild.

The advantage is that we are using "official" front-door methods to manipulate the DOM which is more robust. If we call, for example, document.createElement("tr") then we know that we will get a <tr> without worrying about typos or having to remember to add </tr> at the end.

The disadvantages are firstly, as I mentioned above, a large number of calls to the various DOM methods, nearly 500 in this fairly small table, and secondly this code is much longer than that in the next function listed below.

Finally let's look at some methods provided by table elements.

creatingtables.js part 4: table-specific methods

function createTable3()
{
    const elements = Elements.GetElements();
    const headings = Elements.GetHeadings();
    const properties = Elements.GetProperties();

    // create a table
    const table = document.createElement("table");

    // add a caption
    table.createCaption().textContent = "Elements";

    // insert a row and add headings to it
    const hrow = table.insertRow();
    for(let heading of headings)
    {
        hrow.insertCell(-1).outerHTML = `<th>${heading}</th>`;
    }

    // iterate data, adding rows and cells for each element
    for(let element of elements)
    {
        const drow = table.insertRow(-1);

        for(let property of properties)
        {
            drow.insertCell(-1).innerHTML = element[property];
            // OR
            // drow.insertCell(-1).appendChild(document.createTextNode(element[property]));
        }
    }

    // add table to div
    document.getElementById("tablediv").appendChild(table);
}

The same overall structure here but as you can see the code is much more compact.

We still need document.createElement("table") to create a table but after that we can use methods and properties of the table or its child elements to create the caption, rows and cells.

Note that insertRow and insertCell take an index argument but you can use -1 to insert the row or cell at the end as I have done here.

One slight irritation is that you cannot directly create a <th> as insertCell always gives you a <td>. I have got round this by overwriting the new cell's outerHTML.

When inserting cells there are two ways to set the text, setting innerHTML or creating/appending a text node. I couldn't make up my mind which I prefer: the first is more compact, the second more inkeeping with the spirit of this function's approach. Take your pick.

The same advantages/disadvantages apply: code which is easier to write and maintain versus large numbers of DOM manipulation calls.

Try it Out

If you want to see the results open creatinghtmltables.htm in your browser and run one function at a time by uncommenting the function calls in window.onload. Of course the end result is identical for all three.

Conclusion

So, which to use? There is no definitive answer but I cannot see any advantage in the second option, createElement, createTextNode and appendChild as we have more specialized methods available.

If you have a simple but large data set then building a string is efficient if you don't mind coding all the opening and closing tags and their content, and risking having to untangle the code if the data structure or requirements change. If you have small amounts of data and prefer slick and elegant code then go with option three, createCaption, insertRow and insertCell.

Also please note that createCaption, insertRow and insertCell are only a few of the table-specific methods and I will look at the others in a future article.

Looking for a VPN?

StrongVPN is running a promotion where customers can get a permanent discount on the monthly plan for just $5 for life! Ends June 30, 2020.