Back To The Basics: How To Generate a Table With JavaScript (vanilla)

What it takes to generate a table with vanilla JavaScript? Let’s find out in this tutorial!

Back To The Basics: How To Generate a Table With JavaScript (vanilla)

It’s always a good time for refreshing your JavaScript skills: manipulating the DOM with the native API is a topic that comes up a lot in technical interviews.

In the following tutorial we’ll see what it takes to generate a table with vanilla JavaScript, without resorting to any library or framework.

How to generate a table with JavaScript: what you will learn

In this tutorial you will learn how to:

  • generate a table with JavaScript
  • use the native DOM API for manipulating the table

How to generate a table with JavaScript: requirements

To follow along with this tutorial you should have a basic understanding of HTML, and JavaScript.

How to generate a table with JavaScript: investigating the requirements

Eloquent JavaScript is one of the best books for learning JavaScript. The book is concise, never boring and the exercises make for a great challenge. The following exercise is adapted from chapter 14 and it’s called “build a table”. Well, it’s not a though challenge but …

You’re asked to build an HTML table with JavaScript. Starting from an array of “mountains” your task is to generate the table assigning every key to a column and one row per object.

Every object has the following shape:

{ name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" }

We have a name, an height and a place in which the peak is located in. But what makes an HTML table? An HTML table is an element containing tabular data, presented in rows and columns. That means given the following array:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" }
];

We are expecting to generate the following table:

<table>
    <thead>
    <tr>
        <th>name</th>
        <th>height</th>
        <th>place</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>Monte Falco</td>
        <td>1658</td>
        <td>Parco Foreste Casentinesi</td>
    </tr>
    <tr>
        <td>Monte Falterona</td>
        <td>1654</td>
        <td>Parco Foreste Casentinesi</td>
    </tr>
    </tbody>
</table>

As you can see the table has a thead (table head) containing a tr (table row) which in turn contains three th (table header).

Then there’s the tbody (table body) containing a bunch of tr (table rows). Each table row contains a certain number of td elements (table cells).

With these requirements in place we can start coding our JavaScript file. Our starting point can be the following HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Build a table</title>
</head>
<body>
<table>
<!-- here goes our data! -->
</table>
</body>
<script src="build-table.js"></script>
</html>

Save the file as build-table.html and go ahead to the next section!

How to generate a table with JavaScript: generating the table head

Create a new file named build-table.js in the same folder as build-table.html and start the file with the following array:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
  { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
  { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
  { name: "Monte Amiata", height: 1738, place: "Siena" }
];

Our first goal is to generate the table head. But let’s think a moment about it. We know that the native method createElement() creates whatever element we pass to it. Say we want to create a table head, we can do document.createElement(‘thead’). But do we have a better alternative?

Let’s head over to MDN, at the element table reference. You can see that the DOM interface for table is HTMLTableElement.

The interesting thing for HTMLTableElement is the methods it exposes. Among the methods there is createTHead(). Bingo! createTHead returns the table head element associated with a given table, but better, if no header exists in the table, createTHead creates one for us.

Armed with this knowledge let’s create a function in our file, taking the table as a parameter. Given the table we can create a new thead inside it:

function generateTableHead(table) {
  let thead = table.createTHead();
}

Now let’s grab our table (remember we have one in build-table.html) and pass that to our function:

function generateTableHead(table) {
  let thead = table.createTHead();
}

let table = document.querySelector("table");
generateTableHead(table);

If you call build-table.html in a browser you’ll see nothing on the screen but the developer console will show you a thead right inside the table. We’re half way down to populating the table head. We saw that the table head contains a row filled with a bunch of th (table headers). Every table header must map to a key describing what our data is made of.

The information is already there, inside the first object in the mountain array. We can iterate over the keys of the first object:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
    //
];

and generate three table headers with said keys. But first we need to add a row to our thead! How? document.createElement(‘tr’)? No no. Our HTMLTableRowElement is kind enough to offer an insertRow() method, to be called on our table header. Let’s refactor a bit our generateTableHead function:

function generateTableHead(table) {
  let thead = table.createTHead();
  let row = thead.insertRow();
}

And while we’re there let’s think of populating the table head. The new row should contain three th (table headers). We need to create these th elements manually and for each th (table header) we will append a text node. Our function can take another parameter to iterate over:

function generateTableHead(table, data) {
  let thead = table.createTHead();
  let row = thead.insertRow();
  for (let key of data) {
    let th = document.createElement("th");
    let text = document.createTextNode(key);
    th.appendChild(text);
    row.appendChild(th);
  }
}

let table = document.querySelector("table");
let data = Object.keys(mountains[0]);
generateTableHead(table, data);

Save the file and refresh build-table.html: you should see your table head being populated with name, height and place as table headers. Congratulations! Sometimes it feels so good to take a break from React and Vue just for the sake of recalling how hard and cumbersome direct DOM manipulation is (said no one ever). But stay here! We’re not done yet.

Time to populate the table …

How to generate a table with JavaScript: generating rows and cells

For populating the table we will follow a similar approach but this time we need to iterate over every object in the array of mountains. And while we’re inside the for…of loop we will create a new row for every item.

For creating rows you will use insertRow().

But we cannot stop here. Inside the main loop we need an inner loop, this time a for…in. The inner loop iterates over every key of the current object and in the same time it:

  • creates a new cell
  • creates a new text node
  • appends the text node to the cell

The cells are created with another useful method of HTMLTableRowElement, insertCell().

That is, with the logic above we can populate our table. Open up build-table.js and create a new function named generateTable. The signature can be the same as our existing function:

function generateTable(table, data) {
  for (let element of data) {
    let row = table.insertRow();
    for (key in element) {
      let cell = row.insertCell();
      let text = document.createTextNode(element[key]);
      cell.appendChild(text);
    }
  }
}

To run this function you will call it like so:

generateTable(table, mountains);

Let’s take a look at the complete code:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
  { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
  { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
  { name: "Monte Amiata", height: 1738, place: "Siena" }
];

function generateTableHead(table, data) {
  let thead = table.createTHead();
  let row = thead.insertRow();
  for (let key of data) {
    let th = document.createElement("th");
    let text = document.createTextNode(key);
    th.appendChild(text);
    row.appendChild(th);
  }
}

function generateTable(table, data) {
  for (let element of data) {
    let row = table.insertRow();
    for (key in element) {
      let cell = row.insertCell();
      let text = document.createTextNode(element[key]);
      cell.appendChild(text);
    }
  }
}

let table = document.querySelector("table");
let data = Object.keys(mountains[0]);
generateTableHead(table, data);
generateTable(table, mountains);

Do you think it works? Let’s give it a shot:

How to generate a table with JavaScript: generating rows and cells

Wow. It looks like our rows are being appended to the table head rather than to the table body. Also, there is no table body!

But what happens if we switch the functions order? Let’s try:

// omitted for brevity

let table = document.querySelector("table");
let data = Object.keys(mountains[0]);
generateTable(table, mountains); // generate the table first
generateTableHead(table, data); // then the head

and refresh the browser again:

How to generate a table with JavaScript: generating rows and cells, insertRow

It works! Plus we’got a tbody (table body) for free. How so? When you call insertRow() on an empty table, the methods takes care of creating a tbody for you (if none is present).

Well done! Our code may not be well organized (too many global bindings) but we’ll come to that in one of the next posts.

By now you should be able to manipulate HTML tables without any external library. Congrats!

How to generate a table with JavaScript: wrapping up

In this tutorial we saw how to generate a table with JavaScript. An HTML table is represented in the DOM by the HTMLTableElement. This interface exposes a lot of useful methods for manipulating table heads with createTHead and table rows with insertRow.

HTML table rows on the other hand inherit from HTMLTableRowElement. This interface has two methods, one of the most important being insertCell.

Given an array of objects it is possible to iterate over them with a for…of loop for generating table rows. Then for each object we can iterate with for…in for generating table cells.

We’ve got some basic JavaScript code with too many global bindings (see Execution context and Call Stack for more). In one the next post we’ll see how to refactor the code to a more robust implementation.

jQuery is slowly fading away. Bootstrap will remove it in version 5. The native DOM API is getting better and better. What we used to do with jQuery is doable today without (almost) any additional dependency.

But even without jQuery it’s easy to fall into the trap. It’s been said you shouldn’t manipulate the DOM without $shinyNewLibrary. In reality every serious JavaScript developer should know the native DOM API, and how to manipulate the DOM with JavaScript. These questions come up a lot in technical interviews and you don’t want to be rejected because of that, won’t you?

The code for this tutorial is available on Github.

Thanks for reading and stay tuned!

Leave a Reply

Your e-mail address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.