Piggy bank (variables, localStorage)

The story so far

You have a BS template with some JS to load a navbar, and footer. You know how to make input fields, and buttons. You know how to tie JS code to events. You know how to validate. You know how to use variables to coordinate what happens across events.

Wow! You know a lot of stuff!

Time for a new app. This one is simple. We'll expand it in the next example.

A bit o' tech

We're going to use localStorage. It lets you store data on a user's computer, that a browser can access. For example:

localStorage.setItem("bestAnimal", "dog");

Later, you can get the value with:

bestAnimal = localStorage.getItem("bestAnimal");

You can also remove an item:

localStorage.removeItem("bestAnimal");

bestAnimal is a key that identifies the data. Like a primary key in a database table. localStorage keys are strings.

Each browser has its own localStorage. So data you store in Firefox won't be available to Chrome. Data you store in Firefox on one device, like your laptop, won't be available in Firefox on another device, like your phone, or a friend's laptop. They'll have their own storage.

Browsers give each site its own localStorage. So wickerhope.com and shygerms.net have different localStorage in every browser. However, every page on wickerhope.com, like wickerhope.com/day/tuesday.html, and wickerhope.com/extend/explore/evaluate/emit.html, share the same localStorage. So if the first page puts something in localStorage, the other one will be able to access that data, and vice versa.

Remember how we use variables to coordinate different code fragments? localStorage extends that across pages, and time.

On to the new app.

Requirements

Goals

Let's make an app for a piggy bank. You add pennies to it, and it tells you how much money the pig has.

The total is persistent. So, if you close your browser, do other things, then open your browser up, the total will still be there. It will be there next week. Until you empty the pig.

Screen

There's just one screen. Here's a mock up:

Mock up

Not much to it. The current total, and two buttons.

Events

  • Add button: Add a penny to the total.
  • Reset button: Set the total to zero.
Georgina
Georgina
Seems like there's something missing. The app remembers a total, right? So when the app starts up, won't it have to show the current total?

Yes, good point.

  • App start: show current total.

Pseudocode

There are three events that need code. Let's rough them out.

Event: on page ready

Read total from localStorage
Show the total

So we'll need a variable for the total. Every code fragment will use the variable.

Event: Add penny button clicked

Add 0.01 to total
Store new total in localStorage
Show the total

Event: Empty the pig

Set total to 0
Store new total in localStorage
Show the total

Onward!

HTML

Here's what it will look like:

Start

Here's the HTML:

  1. <h1>Piggy Bank</h1>
  2. <p>Amount: $<span id="amount"></span></p>
  3. <p>
  4.     <button onclick="PiggyBank.addPenny()"
  5.         type="button" class="btn btn-primary"
  6.         title="Put a penny in the pig"
  7.     >Add a penny</button>
  8.       
  9.     <button onclick="PiggyBank.takeFortune()"
  10.         type="button" class="btn btn-danger"
  11.         title="Take the pig's fortune!"
  12.     >Take it all</button>
  13. </p>
The place for output has an id of amount.

<p>Amount: $<span id="amount"></span></p>

Each button has an onclick call.

The shared variable

Let's set up the template, and add the key variable: the total. We'll call it pigFortune, because that's a little funny. OK, only a tiny bit.

  1. <script>
  2.     "use strict";
  3.     var PiggyBank = PiggyBank || {};
  4.     (function($) {
  5.         var pigFortune = 0;
  6.         $(document).ready(function () {
  7.             ...
  8.         });
  9.     }(jQuery));
  10. </script>
I've called the app PiggyBank. Could be BaconBank, Hogworth, something else. As long as we're consistent, it doesn't matter.

The key variable is on line 5:

var pigFortune = 0;

All of the code inside…

(function($) {
    ...
}(jQuery));

… will have access to the variable.

Loading the total

When the app loads, it will grab the current value from localStorage. Here's code:

$(document).ready(function () {
    //Template stuff
    ...
    //Load initial savings.
    //pigFortune has already been initialized to zero.
    if ( localStorage.getItem("pigFortune") ) {
        pigFortune = parseFloat(localStorage.getItem("pigFortune"));
    }
    //Show current pigFortune.
    $("#amount").html(pigFortune);
});

Check out the first part:

if ( localStorage.getItem("pigFortune") ) {
    pigFortune = parseFloat(localStorage.getItem("pigFortune"));
}

This line…

if ( localStorage.getItem("pigFortune") ) {

… checks whether there is a value for the key pigFortune in localStorage. If there is, that value will be put into the variable pigFortune:

pigFortune = parseFloat(localStorage.getItem("pigFortune"));

A problem with localStorage is that it will only store strings, that is, text data. That's why parseFloat() is there. It converts data from a string to a number.

There are two similar functions: parseFloat, and parseInt. The difference is whether the number can have a decimal part. In JS, a float is a number that can have a decimal part, like 3.22. or 133.34. Floats are called singles in some languages.

parseInt always returns an integer, that is, a whole number. It strips off the decimal part, if there is one. So parseInt("3.77") returns 3.

Back to:

if ( localStorage.getItem("pigFortune") ) {
    pigFortune = parseFloat(localStorage.getItem("pigFortune"));
}

If there is nothing with the key pigFortune in localStorage, pigFortune will be left at its current value. What is it? Remember this:

  1. <script>
  2.     "use strict";
  3.     var PiggyBank = PiggyBank || {};
  4.     (function($) {
  5.         var pigFortune = 0;
  6.         $(document).ready(function () {
  7.             ...
  8.             if ( localStorage.getItem("pigFortune") ) {
  9.                 pigFortune = parseFloat(localStorage.getItem("pigFortune"));
  10.             }
  11.             ...
  12.         });
  13.     }(jQuery));
  14. </script>
pigFortune was initialized to zero already.

There's one more line in the page ready code:

  1. $(document).ready(function () {
  2.     //Template stuff
  3.     ...
  4.     //Load initial savings.
  5.     //pigFortune has already been initialized to zero.
  6.     if ( localStorage.getItem("pigFortune") ) {
  7.         pigFortune = parseFloat(localStorage.getItem("pigFortune"));
  8.     }
  9.     //Show current pigFortune.
  10.     $("#amount").html(pigFortune);
  11. });
This code…

$("#amount").html(pigFortune);

… says to "Find something on the page with an id of amount, and set its HTML to the value of pigFortune."

OK, so we've finished the code for the page load event.

Adding a penny

We saw the HTML for the button:

  1. <button onclick="PiggyBank.addPenny()"
  2.     type="button" class="btn btn-primary"
  3.     title="Put a penny in the pig"
  4. >Add a penny</button>
Here's the code for PiggyBank.addPenny():

/**
* Record new savings.
*/
PiggyBank.addPenny = function() {
    pigFortune += 0.01;
    localStorage.setItem("pigFortune", pigFortune.toString());
    $("#amount").html(pigFortune);
};

It start with a comment, explaining what the function does.

The first executable line is:

pigFortune += 0.01;

+= is a shortcut for "add to." So…

pigFortune += 0.01;

… is the same as…

pigFortune = pigFortune + 0.01;

Think of += as "increase by," if you like.

The next line…

localStorage.setItem("pigFortune", pigFortune.toString());

… records the new value of pigFortune in localStorage, under the key pigFortune.

Remember that localStorage can only store strings. pigFortune.toString() does the conversion.

One line left:

  1. /**
  2. * Record new savings.
  3. */
  4. PiggyBank.addPenny = function() {
  5.     pigFortune += 0.01;
  6.     localStorage.setItem("pigFortune", pigFortune.toString());
  7.     $("#amount").html(pigFortune);
  8. };
As we've seen, that changes the HTML of the element with the id of amount.

Taking it all

Here's the screen again.

Start

What about the Take it all button?

/**
* Take all the pig's money.
*/
PiggyBank.takeFortune = function() {
    pigFortune = 0;
    localStorage.removeItem("pigFortune");
    $("#amount").html(pigFortune);
};

It starts with a comment, telling what the function does.

The code sets pigFortune to zero. Remember, that variable is how all of the code in the this app coordinates its work. If the "Add a penny" code runs next, it will be working with the new value of pigFortune, that is, zero.

This…

localStorage.removeItem("pigFortune");

… erases the item with the key pigFortune from localStorage. It doesn't set the item to zero. It removes the item completely.

Finally…

$("#amount").html(pigFortune);

… shows the new value of pigFortune.

Dev tools

Dev tools can show localStorage. Handy for debugging. Here's Chrome's tools:

localStorage in Chrome

Exercise

Exercise: Freddos
Write an app to keep track of your Freddos. It opens like this:

Start

Link the image to https://en.wikipedia.org/wiki/Freddo. The image comes from there.

To position the image, try the Bootstrap classes float-left, and pr-5.

Clicking the buttons adds one to the Freddo count, subtracts one, or sets it to zero.

Note: number of Freddos cannot go below zero.

Make sure the number of Freddos is persistent. When the user reloads the page, or reboots the computer, it will show the last value for the Freddo count.

Submit the URL of your solution.

(If you were logged in as a student, you could submit an exercise solution, and get some feedback.)

CoolThing

Code that jumps back to the previous page, after a random delay.

Summary

  • localStorage let you persist data.
  • Each localStorage item has key. The key is a string.
  • localStorage only stores strings. If you want to store a number, you need to convert the data with .toString(). To get the number back, grab the string form from localStorage, and use parseFloat, or parseInt.