27 Client-side Storage
You now have almost have all the tools you need to create a true client-side web app. You can define an interface in HTML and CSS, using input elements if required, and then add functionality to it in JavaScript. But at the moment, your web apps have no long term memory. They are unable to recognized repeat visitors or remember any user data.
For safety and security, web apps don’t have much access to the client machine. They are sandboxed inside the browser, because it would be far too dangerous to allow code delivered to a client as part of an HTTP Request have full access to their machine!
The most reliable way for web app to access long-term storage is to use a database and related server-side scripts. But the HTML5 web storage system provides a limited way to store data on the client’s machine either temporarily or “permanently” (at least until they clear their cache).
Connections
Desktop apps written in Java, Python, C, or any other language generally have a lot of access to the machine they are running on. They can read from and write to files and databases on the local machine. Because JavaScript programs run on web pages, it would be too dangerous to give them this kind of access to the local machine – some evil hacker could embed JavaScript into a web page to wipe out the user’s hard drive or install malware or ransomware. So JavaScript apps are sandboxed. They are only given access to a very limited piece of the client machine.
Web Storage Basics
Web developers often want their sites to remember users, maintain their settings between page loads, and even keep copies of the data the user entered for the next time they visit. In the Server-side Programming section of this book, we’ll look at how to maintain data entered by the user on the server side. This section is about remembering user data on the client side.
Modern web browsers allow JavaScript access to a localStorage object for long term data storage and a sessionStorage object for temporary data storage. Each domain name gets a single copy of the localStorage object and each open tab (a.k.a. “session”) gets its own sessionStorage object. These objects can store up to 5 MB of data on the client in total for each domain. The data in sessionStorage is deleted when the browser tab is closed, but the data in localStorage is only ever deleted when a JavaScript program decides to delete it, or when the user clears their browser cache. Otherwise, it’s permanent.
Do it Yourself
Go to a large, high traffic site such as Google.com in the Chrome browser. Enter the developer tools and then navigate to the Application tab. Under storage you should be able to see the data that the site has placed into your sessionStorage and localStorage objects. (You can also view Cookies here – more on that in the section on server-side programming.)
Now go to the JavaScript console and type the following to see the same information:
sessionStorage localStorage
The sessionStorage and localStorage objects work just like any other JavaScript object or associative array, except that they can only store string data. Each item of data is stored under a string key or field name, and is accessed with square bracket notation or dot notation.
Do it Yourself
On the same web you used for the last DIY box, go to the JavaScript console and type the following:
localStorage["name"]="Bob"; localStorage.number=22342;
Now reload the page, and then type the following to see what you entered.
localStorage localStorage.name; localStorage["number"];
Notice that the response from the last line is in quotes. This means that the integer you entered was converted to a string before being stored.
Now repeat the above command sequence but using sessionStorage instead of localStorage. You should get the same results.
Now close the browser tab and go back to the web site in a new tab. Check the localStorage and sessionStorage objects to see what data remains. Is it what you expected?
Both sessionStorage and localStorage also contain a removeItem method that can be used to remove a single field and a clear method that can be used to remove all fields at once.
Do it Yourself
Go back to the page from the last DIY box and open the JavaScript console. Type the following:
localStorage.removeItem("name") localStorage.name
Notice that the name field is now gone from the localStorage object.
Now try this:
localStorage.clear() localStorage
Now the localStorage object should be completely empty.
Initializing Web Storage
Suppose you are counting how many times a user visits your site. On the user’s first visit, you will have to create a counter key in localStorage and set it to 1. On subsequent visits, you should not re-create the counter. Instead, you should access it and increment it. So you need a way to figure out if the user has been here before.
The code below accomplishes this by taking advantage of the fact JavaScript’s weak type system treats the special value undefined the same as false in a Boolean expression, and most other values (other than 0, null, and the empty string) as true. If the user has not been here before, the else clause will initialize the counter. Otherwise, the counter will be incremented.
if (localStorage.counter) localStorage.counter = parseInt(localStorage.counter) + 1; else localStorage.counter = 1;
Notice also the use of parseInt above. What would happen if parseInt were left out?
Do it Yourself
Load webStorageDemo.html from the Full Stack Example Pack into a browser to see the above code in action. Hit reload a couple of times to see the counters move. Then open the page again in another tab to see what happens to the counters.
This web page uses localStorage to count the total number of visits and sessionStorage to count the number of return visits in the current tab. If you open the page in a new tab, the session count will start from zero, but even if you close the browser and re-open it, the total count will be remembered.
Connections
JavaScript is a little unusual in only casting 0, null, and “” to False. Most languages that allow implicit type casting to Booleans would allow more “False” values. For example, Python treats any empty or zero value as False. This includes 0, “”, [], {}, and None.
Storing Structured Data
The main limitation of localStorage and sessionStorage objects is that they can only store string data. Storing numbers is relatively straightforward – the type conversions are mostly automatic and we can use parseInt and parseFloat when they are not. But storing more complex data types such as objects and arrays is not as easy.
Do it Yourself
Try this in the JavaScript console:
let student = { name:"Sam Scott", status: "Enrolled", grades: [99, 100, 85] }; localStorage.student = student;
So far, so good. JavaScript’s weak typing allows any value to be converted to a string. But now let’s look at what we got:
console.log(localStorage.student);
As you can see, it didn’t work the way we wanted it to.
Fortunately, a string notation called JSON (JavaScript Object Notation) was developed for the purpose of storing or transmitting structured data as text. We will need it later for client-server communication because the HTTP protocol is a text transmission protocol. Right now, we need it for storage.
JavaScript contains a JSON API containing the methods parse and stringify. The stringify method turns any object into a string (hence the name), while the parse method turns a JSON string back into an object (to “parse” a text means to figure out its structure and meaning).
Do it Yourself
Try stringify in the JavaScript console:
let student = { name:"Sam Scott", status: "Enrolled", grades: [99, 100, 85] }; JSON.stringify(student);
You should see a string representation of the student object in the console.
To store the student in localStorage you can use the following:
localStorage.student = JSON.stringify(student);
To check what we got:
console.log(localStorage.student);
To retrieve the object stored in localStorage, we can use parse:
retrieved = JSON.parse(localStorage.student);
Check the contents of retrieved in the console to make sure that you can access all its fields and array elements.
<exercises chapter=”standalone”>
- Write a simple web app with two pages. On page one, have the user fill in input elements with their name and the color shirt they are wearing. If they have been here before, you should fill in their name automatically. Store their name in localStorage and their shirt color in sessionStorage. Provide a link to page 2. On page 2, you should greet the user by name and tell them whether you like their shirt or not, based on the color they entered last time (for example, write an if statement that only says you like their shirt if they say it was red). Provide a link back to page 1.
- Write a mobile “memo pad” application similar to the one shown below. This app should allow the user to type into a textarea element. A textarea element is similar to an input element with a value attribute, but you create it using <textarea id=”…”></textarea>). If they choose to save the text (perhaps with a button click), the text should be stored in localStorage and displayed when they return for them to read or modify. Make it look professional (like a real app you might see on a mobile phone).
- Add “revert to saved” button that copies the value from localStorage back into the memo app. The Save and Revert buttons should be disabled when the memo contents matches the saved contents, and enabled as soon as they change.
- Now adapt your memo app so that it can store 50 pages using buttons or other input elements to allow the user to flip through the pages. Use localStorage to store an array of pages (in JSON format), to remember what page they were viewing last, and to display the last page they were viewing when they return.
- In a previous chapter you made a shopping list app that allows the user to add and remove items from a shopping list. Now adapt it so that it uses localStorage to keep a copy of the list and displays it when the user returns. You will need to update localStorage every time there is a change to the list.
</exercises>