A Simple JavaScript Weight Converter With Storage Part 2

In part 1 of this 2 part series, I covered creating a simple, functional kilos to pounds and stone converter. Go and look at that if you have found yourself transported here, first.

In this final blog post, I’m going to finish off the most valuable tool by adding in the feature of saving a last conversion to local storage, and styling it a little with Mini.css

Before we begin, paste your eyes onto this screenshot which is what we will end up with:

Adding the HTML Elements

We’re going to need two main things:

  1. A button to save our results and,
  2. A way to display previous saved data

For the first thing, we’ll place a button much like we had with the conversion action button before and for the results, a table should fit the bill nicely. Paste this underneath the previous HTML, but before the end of the BODY tag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<button id="save-weight">Save Result</button>

<h2>Last Weight</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Stones</th>
<th>Pounds</th>
</tr>
</thead>
<tbody>
<tr id="last-weight-results">

</tr>
</tbody>
</table>

Our button has the ID of save-weight - we’ll be using that when we attach to the “click” event. The table, too, isn’t anything complicated, but notice that we have given the table row TR an ID of last-weight-results. I’ll be digging into that a lot more when we come to the JavaScript, which is next.

Adding to the Table

As you may know, the HTML of a page is stored in the DOM - Document Object Model. JavaScript has some really handy functions which will allow you to add and remove from the DOM and that’s what we need when it comes to displaying the results of the last saved weight, dynamically.

We have three values to add:

  1. The date the weight was saved,
  2. The number of stones, and
  3. The number of pounds

For a table, what this translates into are three TD values, which will reside in the TR that is already there. But how do we do that?

Let’s use the date as a specific example. Inside my TR, I want to start with one TD that will look a little like this:

1
2
3
<tr>
<td>3/5/2021</td>
</tr>

which will represent the date: the 3rd of May, 2021 (in the British style of writing dates).

As you can see, that is composed of one TD and some text. To create this in JavaScript, we’re going to use the following functions:

  • document.createElement()
  • …appendChild()
  • document.createTextNode()

createElement() does what it says on the tin, which is create an element. Element in this context is going to be a TD. We’ll be passing in one parameter which is the name of the element: e.g. document.createElement('TD').

Returning from that function is a pointer to the actual node created, but we’re not finished yet. That node just sits in the air, not connected to anything, and certainly not part of the DOM.

Next, we need to create some text which will go into the text portion of that TD node. In this case, this represents the value, and can be achieved by our third function: document.createTextNode(). Here, we just pass in the value as the only parameter a bit like this: document.createTextNode(value).

Now to piece the two together by using the second function. Rather than be a little abstract, here’s the whole function which you need to paste into your SCRIPT block, directly under the convertKilosToStonesAndPounds() function.

1
2
3
4
5
6
const createElementWithValue = (element, value) => {
let nodeElement = document.createElement(element);
nodeElement.appendChild(document.createTextNode(value))

return nodeElement;
}

With this helper function, we pass in our element as parameter one, and the value, as the second. Here’s an example of me using it (but don’t add this to the main code):

1
createElementWithValue('td', '3/5/2021');

Removing Nodes From the Table

So we can create nodes, or elements, but if we keep doing that and add them to the TR row, surely we would end up with hundreds if not tens of millions (ha!) of TD elements? We only want three for this project, so we had better look into how we are going to remove what is there next. For that, I came across this excellent post which shows how to remove all the children under a node. There’s quite a bit to say about how the DOM organises the elements on a page, but suffice to say its mostly either hierarchical or involving siblings (nodes at the same level as each other). Here’s the code, slightly adapted from the author’s blog post.

1
2
3
4
5
const removeAllChildNodes = (parent) => {
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
}

We call this function by passing in the parent node - that would be the TR in this case, which we gave the ID of last-weight-results. How does it work, though?

The function first asks whether there is a child, a first child, under it. If there is, the parent (TR) removes that first child from the DOM. On the next pass of the while loop, that child, were it ever there, is now gone and in its place could be another “first” child. If so, the loop contents remove it, and so on.

We’ve covered adding nodes, and removing them, so let’s now move onto how we’re getting our data for the table - the saving of information into local storage.

Saving Weight Results

1
2
3
4
5
6
7
8
9
10
11
const saveWeight = () => {
localStorage.setItem("last-weight", JSON.stringify(
{
"date": new Date(Date.now()).toLocaleDateString(),
"stones": stoneElement.innerText,
"pounds": poundElement.innerText
}
));

displayLastWeight();
}

saveWeight() is our function that will be attached to the Save Result button and it does two things: actually save the results into local storage and then re-display our table. We haven’t gotten to the display part yet, but we can talk about the saving in a little more detail.

The way I decided to do this was to use a JSON object with the key last-weight. As you can see, localStorage.setItem() takes two parameters. The first is the key we want to use, and the second is the actual value. That’s how cool this method is - all the magic is taken care of for us. In this case, we stringify - turn into a string - the JSON object so that it can be stored properly.

The actual object we are keeping has three keys:

  • date
  • stones and,
  • pounds

The date is just a pretty-printed string for the current date, and the values are pulled back out of the OUTPUT tags we created in the last post. That’s it - nothing more than that.

Displaying the Table

Here’s the code to display the results pulled out of local storage, should there be any.

1
2
3
4
5
6
7
8
9
10
11
12
const displayLastWeight = () => {
let lastWeight = localStorage.getItem("last-weight");
lastWeight = JSON.parse(lastWeight);

if (lastWeight !== null) {
lastWeightResults = document.getElementById("last-weight-results");
removeAllChildNodes(lastWeightResults);
lastWeightResults.appendChild(createElementWithValue("td", lastWeight.date));
lastWeightResults.appendChild(createElementWithValue("td", lastWeight.stones));
lastWeightResults.appendChild(createElementWithValue("td", lastWeight.pounds));
}
}

The first line pulls the value that may have been stored into local storage. It’s then turned back into a JSON object with the JSON.parse() function.

Next, we check to see whether did in fact get anything back out of local storage. If not, we pass till next time.

If there was something stored, we first empty out any children of the TR, before adding the TD elements back again. One for date, one for stones and the last one for pounds. Much like removeChild() there is a similar, opposite function, to add them back in: appendChild(), which I briefly mentioned earlier.

Finishing Off the Functionality

The last things to do are to hook up an event listener for “click”s on the Save Results button and display anything (if possible) in the table.

1
2
document.getElementById("save-weight").addEventListener("click", saveWeight);
displayLastWeight();

The CSS

This is the best part - we’re going to use Mini.css as we mentioned and once you do, you will see what a huge difference it makes with almost no effort on our parts.

First add in the link to the CSS in the HEAD section.

1
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.1/mini-default.min.css">

I’ve chosen to use a CDN for this.

And to add a little dash of colour, let’s place a class (primary) on the buttons to make them blue: #convert and #save-weight.

1
class="primary"

Open up your final HTML file and it should all work as expected.

Taking it Further

There are of course many things you could do to improve this. Here are a few ideas:

  • Separate the JavaScript into its own file
  • Store more than just the last calculated value into local storage
  • Convert from kilos => stones and pounds, to other weights and measures

If you’d like to see the finished version, you can find it here.

Hope you found this useful and perhaps learnt something. I’m off to weigh myself after eating an apple turnover!


Hi! Did you find this useful or interesting? I have an email list coming soon, but in the meantime, if you ready anything you fancy chatting about, I would love to hear from you. You can contact me here or at stephen ‘at’ logicalmoon.com