Wednesday, January 18, 2023

Local Storage and Session Storage in JavaScript

Local Storage and Session Storage in JavaScript

Let's say you are creating a web app where users will be able to work either with textual data or media like images. You want to allow them to write some text that needs be accessible even after a page refresh or a browser restart. Before the Web Storage API, you would have had to store the information on back-end and reload it back on client side whenever needed. This is still the way to go if you want the information to be available across browsers or devices.

However, if the information that you want to preserve across page refreshes or browser restarts is meant to be accessible only on the same browser, the Web Storage API is a much more suitable tool.

There are two types of web storage implementations called localStorage and sessionStorage. As you might have guessed from the name, sessionStorage preserves information for a single session and localStorage preserves your data even after you restart the browser.

In this tutorial, we will learn all the basics of the Web Storage API and you will see how we can use local storage and session storage to our advantage.

Difference Between Local Storage and Session Storage

Before taking a deep dive into the API, let's learn the basic differences between local storage and session storage.

  1. localStorage doesn't expire even on a browser restart while sessionStorage only lasts till a page refresh.
  2. localStorage is shared across multiple tabs and windows that have the same origin. On the other hand, sessionStorage will be different for each tab that has loaded the same webpage.

A single webpage or document can have its own localStorage as well as sessionStorage object. However, both of them will be independent of each other.

Available Web Storage Methods and Properties

There are five methods that are available for both localStorage and sessionStorage.

You can use the setItem(key, value) method to store some information in the storage object as a key/value pair. If the key already exists, this method will update its value. Keep in mind that this method requires both key and value to a strings.

You can use the getItem(key) method to retrieve the information stored for a particular key. This method will return null if the passed key does not exist.

Let's say you want to remove some information from your localStorage or sessionStorage, in this case, you can use the removeItem(key) method and pass the relevant key name in order to remove a key and its value from the storage.

Instead of removing keys from your storage one at a time, you can also use the clear() method to clear all the keys at once.

There is also a key(index) method that accepts an integer as the key index and gives you back the name of the key at that particular index. The important thing to remember here is that the order of keys is defined by the user-agent.

Finally, there is a length property that you can use to get the number of data items stored in a given storage object.

You can use the length property in combination with the key() method and the getItem() method in order to access the values of all keys in your localSotrage or sessionStorage.

Here are some examples of using all these methods:

1
/* Save some key-value pairs */
2
localStorage.setItem("artist", "Monty Shokeen");
3
localStorage.setItem("website", "tutsplus.com");
4
localStorage.setItem("font", "Righteous");
5
localStorage.setItem("stroke_width", "4");
6
localStorage.setItem("color", "#FF5722");
7
8
/* Access stored values */
9
console.log(localStorage.getItem("color"));
10
// Outputs: #FF5722

11
12
console.log(localStorage.getItem("stroke_width"));
13
// Outputs: 4

14
15
/* Iterate over keys */
16
for (let i = 0; i < localStorage.length; i++) {
17
  console.log(`${localStorage.key(i)} : ${localStorage.getItem(localStorage.key(i))}`);
18
}
19
/*

20
stroke_width : 4

21
font : Righteous

22
website : tutsplus.com

23
color : #FF5722

24
artist : Monty Shokeen

25
*/
26
27
/* Removing keys from storage */
28
localStorage.removeItem("website"); 
29
localStorage.getItem("website"); 
30
// Outputs: null

Practical Application of Local Storage

Let's do something practical with all the knowledge we have gained. We will create a simple drawing application where users will be able to save their data in the local storage for retrieval in the future.

Our drawing application is going to be very simple. We will have a canvas where users will be able to draw concentric circles of random radius. The minimum and maximum value of the radius will be determined by input fields filled by them. We will also have a button to clear the canvas once we have drawn too many circles. Here is our markup:

1
<canvas id="canvas" width="810" height="400"></canvas>
2
<form>
3
  <label for="min-rad">Min. Radius</label>
4
  <input type="number" name="min-rad" id="min-rad" min="4"></input>
5
  <br>
6
  <label for="max-rad">Max. Radius</label>
7
  <input type="number" name="max-rad" id="max-rad" min="20"></input>
8
  <br>
9
  <button type="button" id="clear">Clear Canvas</button>
10
</form>

We will be storing three pieces of information to our local storage: the minimum radius, the maximum radius and the state of canvas. Remember that local storage can only store information as strings. The value of input fields can be automatically converted to strings. However, we will need to use the toDataURL() method to get the state of our canvas as a string. This method will give us back a string that contains the requested data URL.

We will attach event listeners to all the elements on the webpage. A mousedown event listener for the canvas. A change event listener for the input elements and a click event listener for the buttons. Let's start with some initialization code and the event listeners for the form fields.

1
const canvas = document.getElementById("canvas");
2
const ctx = canvas.getContext("2d");
3
4
const minElem = document.querySelector("input#min-rad");
5
const maxElem = document.querySelector("input#max-rad");
6
const clearBtn = document.querySelector("button#clear");
7
8
let min_radius = 10;
9
let max_radius = 30;
10
11
minElem.addEventListener("change", function(event) {
12
  min_radius = parseInt(event.target.value);
13
  localStorage.setItem("min-radius", min_radius);
14
});
15
16
maxElem.addEventListener("change", function(event) {
17
  max_radius = parseInt(event.target.value);
18
  localStorage.setItem("max-radius", max_radius);
19
});
20
21
clearBtn.addEventListener("click", function(event) {
22
  ctx.clearRect(0, 0, canvas.width, canvas.height);
23
  
24
  let image_data = canvas.toDataURL();
25
  localStorage.setItem("image-data", image_data);
26
});

By default, we keep the minimum and maximum radius values set to 10 and 30 pixels respectively. The change event listener for the minimum and maximum radius input fields will parse the value of inputs and then store those values in local storage.

Within the click event listener callback for our button, we first cleared the canvas and then saved this cleared state to our local storage using the toDataUrl() method.

Here is the code that listens to the mousedown event on our canvas.

1
canvas.addEventListener('mousedown', function(event) {
2
    const canvas_rect = event.target.getBoundingClientRect();
3
    const pos_x = event.clientX - canvas_rect.left;
4
    const pos_y = event.clientY - canvas_rect.top;
5
  
6
    for(let i = 0; i < 10; i++) {
7
      let radius = min_radius + Math.floor(Math.random()*(max_radius - min_radius));
8
      ctx.beginPath();
9
      ctx.arc(pos_x, pos_y, radius, 0, 2 * Math.PI);
10
      ctx.stroke();
11
    }
12
  
13
    let image_data = canvas.toDataURL();
14
    localStorage.setItem("image-data", image_data);
15
});

Let's break it down. We begin by calculating the exact position where users clicked on the canvas. This is determined by subtracting the value of the left property of the bounding rectangle of the canvas from the x coordinate of the clicked position. We do the same thing to get the vertical position of the click.

After that, we create a for loop to draw 10 concentric circles on the canvas. The radius is set to a random value subject to the minimum and maximum constraints. Finally, just like the click listener for the button, we save the canvas state in our local storage. This happens with each click so that we can stay up-to-date with the latest canvas state.

The only thing left for us to do now is restoration of values from the local storage to be used on reloads or restarts. We do this with the following code:

1
window.addEventListener("DOMContentLoaded", (event) => {
2
  if (localStorage.getItem("image-data")) {
3
    var img = new Image();
4
    img.onload = function () {
5
      ctx.drawImage(img, 0, 0);
6
    };
7
    img.src = localStorage.getItem("image-data");
8
  }
9
10
  if (localStorage.getItem("min-radius")) {
11
    min_radius = parseInt(localStorage.getItem("min-radius"));
12
  }
13
14
  if (localStorage.getItem("max-radius")) {
15
    max_radius = parseInt(localStorage.getItem("max-radius"));
16
  }
17
18
  minElem.value = min_radius;
19
  maxElem.value = max_radius;
20
});

The most complicated section here is the restoration of image data from the local storage to the canvas. We do this by first creating a new instance of HTMLImageElement and then listening to its onload event in order to draw the loaded image on canvas.

The following CodePen demo will show you our drawing application in action. Try clicking on the canvas to draw some circles or set the radius to values you like first.

Right now, we are using localStorage in our tutorial which means that our data will be safe even if the browser restarts. You can try replacing it with sessionStorage to preserve the information only during page refreshes.

Final Thoughts

In this tutorial, we covered the basics of localStorage and sessionStorage in JavaScript. You should now be able to store and retrieve information in browser's storage using the web storage API. This API has a lot of applications as we saw while creating our drawing app here. You can also use it to implement content saving functionality in a local text editor.


No comments:

Post a Comment