How to use Local Storage with JavaScript

It has been a bit since I did another Let’s Build: With JavaScript so I’m back with a new one focusing in on using Local Storage.

As a precursor to this tutorial I highly recommend not using local storage for comment data as I’m about to show you but rather more for small key-value assignments. For example, say you display a notification prompt, email subscription prompt, or alert of some kind on your site. You can hook into the browsers local storage API to save whether or not a given user wants to see these things as they display on your site.

That all could take place with a click event with JavaScript.

This tutorial goes one step further to show a proof of concept around adding a new comment to an existing comment feed. This in no way is complete but gives you an overview of how to both append new data to the DOM as well as store that data with local storage.

I invite you to extend this code to allow the commenter to delete their comments from local storage as well.

Download the source code

The Markup

I’ll be using some dummy data for starters. We will create a comment feed with one comment already in view. Below the feed will be a new comment form that once submitted will create and insert the value of the new comment on the page. This will also hook into local storage to save the persisted data so when the commenter navigates away from the page and comes back, the data is still present.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Let's Build with JavaScript - Local Storage Comments</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="comment-container">
      <h3 class="comment-container-title">Comments</h3>
      <div class="comments">
        <div class="comment flex items-start justify-start">
          <img
            class="comment-avatar"
            src="https://secure.gravatar.com/avatar/d1f5ca0d7e625f334c5186e112b77ebd"
          />
          <div class="flex-1">
            <h3 class="comment-author">Andy Leverenz</h3>
            <p class="comment-body">First comment of some type!</p>
          </div>
        </div>
      </div>
      <div class="comment comment-new flex items-start justify-start">
        <img
          class="comment-avatar"
          src="https://secure.gravatar.com/avatar/d1f5ca0d7e625f334c5186e112b77ebd"
        />
        <div class="flex-1">
          <h3 class="comment-author">Andy Leverenz</h3>
          <form action="#" class="comment-form">
            <textarea
              placeholder="Add a comment"
              class="comment-input"
            ></textarea>
            <input type="submit" class="comment-submit" value="Reply" />
          </form>
        </div>
      </div>
    </div>

    <script src="app.js"></script>
  </body>
</html>

The CSS

We ought to make the form and comment feed more presentable. In doing so I created the following CSS.

* {
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  font-size: 1rem;
  line-height: 1;
}

.flex {
  display: flex;
}

.flex-1 {
  flex: 1;
}

.items-start {
  align-items: start;
}

.justify-start {
  justify-content: flex-start;
}

.comment-container {
  border-radius: 8px;
  border: 1px solid #e2e2e2;
  margin: 3rem auto;
  max-width: 600px;
  min-height: 200px;
}

.comment-container-title {
  background: #f8f8f8;
  border-bottom: 1px solid #ebebeb;
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
  color: #2d3748;
  margin-top: 0;
  padding: 1rem 2rem;
}

.comment {
  margin-top: 1rem;
  padding: 1rem 2rem;
}

.comment-new {
  border-top: 1px solid #ebebeb;
  margin: 1rem 0;
  padding-top: 2rem;
}

.comment-avatar {
  border-radius: 50%;
  height: 48px;
  margin-right: 1rem;
  width: 48px;
}

.comment-author {
  font-size: 1rem;
  margin-bottom: 0.5rem;
  margin-top: 0;
}

.comment-body {
  color: #4a5568;
  line-height: 1.4;
  margin: 0;
  padding-right: 1rem;
}

.comment-input {
  border-radius: 8px;
  border: 1px solid #dddd;
  box-sizing: border-box;
  color: #4a5568;
  font-size: 1rem;
  line-height: 1.4;
  padding: 0.8rem;
  width: 100%;
}

.comment-input:focus {
  border-color: #5c6ac4;
  outline: none;
}

.comment-submit {
  background: #5c6ac4;
  border-radius: 4px;
  border: none;
  color: white;
  cursor: pointer;
  font-size: 12px;
  letter-spacing: 0.05rem;
  margin-top: 0.5rem;
  padding: 10px 16px;
  text-transform: uppercase;
}

.comment-submit:hover,
.comment-submit:focus {
  filter: brightness(110%);
}

JavaScript

Finally, we make it all work. Even though there is a form element on the page I want to disable its default action. We can do that by adding an event listener on the Reply button. That button will also kick off the process of adding the newly created comment to the DOM itself.

From there we adhere to the template using ES6 string literals and pass in the appropriate data. The data could be much more dynamic but to do that we’d need to either leverage a remote API or a datastore. I commonly work with Ruby on Rails so I would probably reach for the latter and output it somewhere in the markup to later grab with JavaScript directly.

In the end, my code looks like the following. I’ve added comments to be more clear.

const submit = document.querySelector('.comment-submit');
const commentList = document.querySelector('.comments');
const commentInput = document.querySelector('.comment-input');

function template(data) {
  commentList.insertAdjacentHTML("beforeend", `
  <div class="comment flex items-start justify-start">
      <img class="comment-avatar" src="${data.avatar}" />
      <div class="flex-1">
        <h3 class="comment-author">${data.author}</h3>
        <p class="comment-body">${data.comment}</p>
      </div>
    </div>
  </div>`);
}

function appendComment (event) {

  const data = {
    avatar: "https://secure.gravatar.com/avatar/d1f5ca0d7e625f334c5186e112b77ebd",
    comment: commentInput.value,
    author: "Andy Leverenz"
  };

  event.preventDefault();
  // If the value is nothing just return
  if (commentInput.value.length < 1) return;

  // Insert new template into DOM
  template(data);

  // Reset textrea value
  commentInput.value = "";

  // Save the list to localStorage
  localStorage.setItem('commentListing', commentList.innerHTML);
}

submit.addEventListener('click', appendComment, false)

// Check for saved wishlist items
const saved = localStorage.getItem('commentListing');

// If there are any saved items, update our list
if (saved) {
  commentList.innerHTML = saved;
}

Again I invite you to extend this further. Maybe see if you can figure out a way to delete a comment from local storage as well. You might be able to call localStorage.removeItem('my-item, <string>) the same way it was created πŸ˜‰

The series so far