Session 24: Generating HTML from Data

Download this starter code to use during the session.

Student Assessments

Complete student assessments.

Setup First Task: IDS Bundles

Over the last few sessions, we have occasionally discussing how to use JavaScript to generate HTML elements for a page.

Even without an event in the browser, we can use JavaScript to generate HTML elements and add them to a page. We might prefer this approach to writing HTML by hand when the element, often a list or a table, is long and tedious to create.

So far, our examples have all been small:

In Session 23, we saw a more example in the photo gallery web page, in the div with the anchors and images. It contains a lots of repetitive code, which is tedious to write, even with copy-and-paste.

But we could write those by hand!

How about the table of baby names we saw earlier this semester?. It contains 100 rows of five columns each.

That is worth generating.

This week's reading discusses some of the JavaScript tools we can use to access all that, working with strings and arrays.

Let's tackle a smaller problem first: generating an HTML list with all of the "bundles" available in UNI's Interactive Digital Studies program. We encountered these bundles when we learned how to create collapsible lists in Session 13. There, we worked with only three of the ten bundles, and even then the copying and pasting was tedious and prone to typos.

We can find a list of the bundles and their courses in the UNI catalog. I have the full list in a text file that lists each bundle on a separate line in CSV format.

Our goal is to produce HTML for a list like this, but with all ten bundles included.

Let's do it.

Creating a List of IDS Bundles

Let's start with this page. It has a title, an empty main, and a footer. We will generate a ul element and add it to the main section.

Create list of bundles

First, load the data into the script as a string. Use backticks to define a multi-line string. This string is long. If we store it in a variable, it has to appear at the top of the file, where it will obscure code the code that follows. Instead, let's turn it into a function. We can place function definition at the bottom of the file.

function listData() {
  return `Digital Visualization, COMM DM 1611, COMM DM 3659, COMM DM 4655, COMM 3558, COMM 4558
...
Digital Mapping, GEOG 1310, GEOG 2320, GEOG 2410, GEOG 4335, GEOG 4360`;
}

Version 0

Now, let's use split('\n') to create a list of rows, each a string. Then, for each row, use split(',') to create a list of fields.

Find main in the document, and make a ul element that we can build.

Next, loop through the rows, create an li element, set its text to the value of item 0 in the array, and add the element to the to the ul element.

Finally, add the ul element to main.

// split the big string into a list of rows (strings)
let rows = listData().split('\n');

// split each row into a list of strings, trimmed
for (let i = 0; i < rows.length; i++) {
  rows[i] = rows[i].split(',');
}

let mainElement = document.querySelector("main");
let ulElement = document.createElement("ul");

for (let i = 0; i < rows.length; i++) {
  let liElement = document.createElement("li");
  liElement.innerText = rows[i][0];
  ulElement.appendChild(liElement);
}

mainElement.appendChild(ulElement);

Version 1

We can combine the loops, if we'd like. This will create the list item right after we split string into an array. Be sure to move the find/create lines before the first loop, and then merge the loops.

Create sub-lists of bundle requirements

Inside the loop that creates the li elements, make an inner ul element. Loop through the rest of the row's array, starting at 1. For each field, create an inner li and add it to the inner ul. Finally, add the inner ul element to outer li element.

  // inside the for-loop

  let innerUL = document.createElement("ul");
  for (let j = 1; j < rows[i].length; j++) {
    let innerLI = document.createElement("li");
    innerLI.innerText = rows[i][j];
    innerUL.appendChild(innerLI);
  }
  liElement.appendChild(innerUL);

Version 2

There are leading spaces on the strings that are the bundle requirements. They were left behind after the split(','), because the data file had spaces.

This is not a problem here, because the browser will ignore any white space in the inner text of an element. In other situations, though, they could be a problem. It's good to be in the habit of trimming leading and trailing spaces from data fields, using the trim() method.

  innerLI.innerText = rows[i][j].trim();

Version 3

In the end, this code may look complex. But you understand all of the parts. Even better, if we build the script step by step, each addition to the code seems reasonable. The complexity grows out of the individual pieces.

Setup Second Task: Iowa Cities

We could almost write the HTML for that list by hand.

But the table of baby names we saw earlier is much too long to type.

And then consider this table of Iowa cities from Session 6. I used a 4-row, 3-column subset of this list there and in Session 22. The complete table contains 1021 rows with 8 columns each.

No one wants to type this table by hand.

Knowing JavaScript, we do not have to you. The web page has a link to download the data from that website using the download icon. It gives us a CSV file. You can use that file to generate the table.

Exercise: Generate the Iowa Cities Table

Start with this page. It has a title and a table with a caption, a header, a footer, and an empty tbody. You will generate the rows of the table and add them to the tbody element.

The table columns correspond to columns 8, 19, 0, 13, and 15 in the CSV file.

Generate the HTML tr elements to fill the table.

Use the contents of the us-cities-table.csv file to create the multiline string you will process. Put it in a function at the bottom of the file, as we did for the IDS bundles.

Follow the same process we did on that example:

  • create the string
  • split string into an array of rows
  • loop over that array to split each array into an array of fields, creating td elements and adding them to a tr element
  • add the row to the tbody element

Hint: Start your main loop at i = 1, in order to skip the header row.

A Possible Solution

Here is a possible solution: script.js .

Notice that our data has double quotes on the city names. This is surprisingly common in CSV files, to guard against cases in which the data value has a comma in it.

We can use the string method replace to find the "s and replace them with the empty string.

rows[i][8].replace(/"/g, '');     // Remove double quotes

This is a long list, which results in a long scroll that hides the page footer. In Session 11, we learned about CSS's overflow property. We can use it to implement a quick fix to our page:

table {
  display: block;
  height: 510px;
  overflow: auto;
}

Another nice change might to "fix" the header row at the top of the table and scroll only the body.

Bonus: Some data sources provide data in JSON format. JSON data is a JavaScript list of objects!

Bonus: It would be nice to read the data file into a string, rather than do it ourselves. JavaScript supports this, but web browsers do not. They prevent web pages from accessing local files on the client's computer, for security reasons. However, you can run such code on the server or in Node.js.

Closing

At the end of the session, zip up your 24-starter folder and submit it for a quick review using the course submission system. Again, I am not grading these for correctness, only to provide a little extra motivation to follow along in class.

Homework 10 is due tomorrow. It asks you to practice write functions and scripts that modify a web page. This is our last weekly homework assignment.

For the remainder of the course, you will work on a final project. You will implement a small website using all of the tools you have learned this semester: HTML, CSS, and JavaScript. You get to decide what to design and build! I will offer some suggestions in case you don't have anything in mind. Watch for the spec to go live in the next day or two.

Next two session we will practice JavaScript and learn more about the kind of events our scripts can handle. Our second midterm exam 2 is one week from today.