Objective
Create two variants of a function to build an HTML table based on a given array of items.
Description
Consider the following array of objects:
var languages = [
{name: 'JavaScript', releaseDate: 1995, fileExtension: '.js', creator: 'Brendan Eich'},
{name: 'Java', releaseDate: 1995, fileExtension: '.java', creator: 'James Gosling'},
{name: 'PHP', releaseDate: 1995, fileExtension: '.php', creator: 'Rasmus Lerdorf'},
{name: 'C++', releaseDate: 1985, fileExtension: '.cpp', creator: 'Bjarne Stroustrup'},
];
Each object represents information of a particular programming language, such as its name, its release date, its creator, its file extension, and so on.
If we were to convert this array into a table, we'd get the following:
Name | Release Date | File Extension | Creator |
---|---|---|---|
JavaScript | 1995 | .js | Brendan Eich |
Java | 1995 | .java | James Gosling |
PHP | 1995 | .php | Rasmus Lerdorf |
C++ | 1985 | .cpp | Bjarne Stroustrup |
Notice the headings here. They are just the keys of the objects in the array, converted from camel case to title case. Apart from the table's header, each subsequent row represents an item of the array.
In this exercise, you have to define two variants of a function createTable()
that creates a <table>
based on a given array, as demonstrated above.
The function accepts two arguments: the first one is the array to use to create a table whereas the second argument is an element node where we wish to insert the table created.
Here's what the variants are about.
Variant 1
In the first variant of createTable()
, you MUST ONLY use DOM mutation properties, such as innerHTML
and textContent
. You MUST NOT use any DOM mutation method such as appendChild()
, insertBefore()
, etc.
Variant 2
In the second variant of createTable()
, you have do the opposite of variant 1.
That is, you MUST NOT use any DOM mutation properties, such as innerHTML
and textContent
. Instead, you MUST ONLY use DOM mutation methods such as appendChild()
, insertBefore()
, etc.
Both these variants only differ in their underlying implementation; the result produced is exactly the same.
New file
Inside the directory you created for this course on JavaScript, create a new folder called Exercise-45-Building-Tables and put the .html solution files for this exercise within it.
Solution
Let's start by setting up the basic wireframe of the function, i.e. its definition including the parameters required:
function createTable(arr, element) {
// Code to go here.
}
There isn't really much more to explore here, so let's dive right into implementing the first variant of the function.
Variant 1
Step one is to create the header of the table, using the keys of any object from the given array arr
(we could use any item since, as per our assertion, all the items have the exact same keys in the exact same order).
There are technically two ways to accomplish this:
- Use a
for
loop to iterate over a given object and create a<th>
element for each key (ignoring its value). - Call the static method
Object.keys()
to obtain an array of all the keys of a given object.
We'll go with the latter here since it makes the implementation of the rest of the function a little bit simpler. We'll see how as this discussion progresses.
So, let's go ahead and create an array called keys
holding the return value of Object.keys()
as called on the first item of arr
:
function createTable(arr, element) {
var keys = Object.keys(arr[0]);
}
Great!
Now we ought to create the table's header using this keys
array.
And for that, we'll employ the map()
and join()
array methods in addition to the camelToTitleCase()
function, that we created in the JavaScript Arrays — Camel To Title exercise, in order to convert the keys from camel case to title case.
The way these utilities will be used is detailed as follows:
map()
would be used to map each key ofkeys
to a<th>
element containing the key as its text content, converted into title case with the help of thecamelToTitleCase()
function.join()
would join this mapped array with the delimiter''
. This would simply form a string such as'<th>Key 1</th><th>Key 2</th>...'
.
In the end, the output of join()
would be sandwiched in between the strings '<tr>'
and '</tr>'
, and in this way we'd get the header row of the table.
Seems simple to implement, doesn't it?
The code below accomplishes all of this:
function createTable(arr, element) {
var keys = Object.keys(arr[0]);
var html = '<table>';
// Create the heading of the table.
html += '<tr>' + keys.map(function(key) {
return '<th>' + camelToTitleCase(key) + '</th>';
}).join('') + '</tr>';
}
Perfect so far.
Now the next step is to create the rest of the rows of the table. And this just requires us to iterate over arr
and create a table row for each object.
In the code below, we accomplish this using a similar map()
and join()
logic that we used above:
function createTable(arr, element) {
var keys = Object.keys(arr[0]);
var html = '<table>';
// Create the header of the table.
html += '<tr>' + keys.map(function(key) {
return '<th>' + camelToTitleCase(key) + '</th>';
}).join('') + '</tr>';
// Create the rest of the rows.
html += arr.map(function(item) {
return '<tr>' + keys.map(function(key) {
return '<td>' + item[key] + '</td>';
}).join('') + '</tr>';
}).join('');
}
In the end, we just need to concatenate the ending tag '</table>'
to the variable html
and then set the innerHTML
property of element
to html
:
function createTable(arr, element) {
var keys = Object.keys(arr[0]);
var html = '<table>';
// Create the header of the table.
html += '<tr>' + keys.map(function(key) {
return '<th>' + camelToTitleCase(key) + '</th>';
}).join('') + '</tr>';
// Create the rest of the rows.
html += arr.map(function(item) {
return '<tr>' + keys.map(function(key) {
return '<td>' + item[key] + '</td>';
}).join('') + '</tr>';
}).join('');
html += '</table>';
element.innerHTML = html;
}
And this is the implementation of the first variant of createTable()
.
Let's try it out.
In the following code, we have a #main
element where we create a table based on the same languages
array that we saw at the start of this page:
<div id="main"></div>
function camelToTitleCase(str) {
/* ... */
}
function createTable(arr, element) {
/* ... */
}
var languages = [
{name: 'JavaScript', releaseDate: 1995, fileExtension: '.js', creator: 'Brendan Eich'},
{name: 'Java', releaseDate: 1995, fileExtension: '.java', creator: 'James Gosling'},
{name: 'PHP', releaseDate: 1995, fileExtension: '.php', creator: 'Rasmus Lerdorf'},
{name: 'C++', releaseDate: 1985, fileExtension: '.cpp', creator: 'Bjarne Stroustrup'},
];
var mainElement = document.getElementById('main');
createTable(languages, mainElement);
Over to the second variant.
Variant 2
As stated in the exercise's description above, in the second variant of createTable()
, we have to operate solely around nodes; we can't work with strings to create the table.
So, the tools that we have in our inventory right now are document.createElement()
, document.createTextNode()
, and the appendChild()
method of the Element
interface.
insertBefore()
, append()
, insertAdjacentElement()
, and so on. But appendChild()
is clearly simpler and likewise we'll go with it.Let's start coding.
As for the keys
array defined above, we'll omit it in favor of using the for...in
loop to iterate over each object of arr
. This is because we won't be map()
ping or join()
ing any arrays and therefore there really isn't any point of having one.
Anyways, the first thing we'll do is create a <table>
element node to hold all the nodes that we'll be creating later on in our code:
function createTable(arr, element) {
var tableElement = document.createElement('table');
}
Now let's get done with the header of the table:
function createTable(arr, element) {
var tableElement = document.createElement('table');
// Create the header of the table.
var tableRowElement = document.createElement('tr');
for (var key in arr[0]) {
var tableHeadingElement = document.createElement('th');
tableHeadingElement.appendChild(
document.createTextNode(camelToTitleCase(key)));
tableRowElement.appendChild(tableHeadingElement);
}
tableElement.appendChild(tableRowElement);
}
Next up, let's get done with the rest of the rows of the table.
function createTable(arr, element) {
var tableElement = document.createElement('table');
// Create the header of the table.
var tableRowElement = document.createElement('tr');
for (var key in arr[0]) {
var tableHeadingElement = document.createElement('th');
tableHeadingElement.appendChild(
document.createTextNode(camelToTitleCase(key)));
tableRowElement.appendChild(tableHeadingElement);
}
tableElement.appendChild(tableRowElement);
// Create the rest of the rows.
for (var i = 0, len = arr.length; i < len; i++) {
tableRowElement = document.createElement('tr');
for (var key in arr[i]) {
var tableDataElement = document.createElement('td');
tableDataElement.appendChild(document.createTextNode(arr[i][key]));
tableRowElement.appendChild(tableDataElement);
}
tableElement.appendChild(tableRowElement);
}
}
In the end, what's left is just to insert tableElement
inside element
. And this is as simple as calling element.appendChild()
, passing in tableElement
as an argument:
function createTable(arr, element) {
var tableElement = document.createElement('table');
// Create the header of the table.
var tableRowElement = document.createElement('tr');
for (var key in arr[0]) {
var tableHeadingElement = document.createElement('th');
tableHeadingElement.appendChild(
document.createTextNode(camelToTitleCase(key)));
tableRowElement.appendChild(tableHeadingElement);
}
tableElement.appendChild(tableRowElement);
// Create the rest of the rows.
for (var i = 0, len = arr.length; i < len; i++) {
tableRowElement = document.createElement('tr');
for (var key in arr[i]) {
var tableDataElement = document.createElement('td');
tableDataElement.appendChild(document.createTextNode(arr[i][key]));
tableRowElement.appendChild(tableDataElement);
}
tableElement.appendChild(tableRowElement);
}
element.appendChild(tableElement);
}
And this completes the implementation of the second variant of createTable()
.
Now for the testing part, we'll use the same code that we used above, shown as follows:
<div id="main"></div>
function camelToTitleCase(str) {
/* ... */
}
function createTable(arr, element) {
/* ... */
}
var languages = [
{name: 'JavaScript', releaseDate: 1995, fileExtension: '.js', creator: 'Brendan Eich'},
{name: 'Java', releaseDate: 1995, fileExtension: '.java', creator: 'James Gosling'},
{name: 'PHP', releaseDate: 1995, fileExtension: '.php', creator: 'Rasmus Lerdorf'},
{name: 'C++', releaseDate: 1985, fileExtension: '.cpp', creator: 'Bjarne Stroustrup'},
];
var mainElement = document.getElementById('main');
createTable(languages, mainElement);
Amazing. It works flawlessly.
We must be proud of ourself to come this far as to be able to work with the DOM API in different ways and produce the exact same end result.
Great job! 👍