Simple Shopping List In Vanilla Javascript (Free Source Code)

Welcome to a tutorial on how to create a simple shopping list in vanilla Javascript. Nope, I totally did not get the idea for this tutorial from a certain Javascript challenge. It’s original content. 😆 Anyway, a shopping list is totally possible these days with modern Javascript, using the local storage API – That is what we will be using in this guide. Read on to find out!

 

 

TABLE OF CONTENTS

 

JAVASCRIPT SHOPPING LIST

All right, let us now get into the details of the Javascript shopping list itself.

 

SHOPPING LIST DEMO

 

 

PART 1) SHOPPING LIST HTML

1-shop-list.html
<h1>SHOPPING LIST</h1>
<div id="shop-wrap">
  <!-- (A) ADD NEW ITEM -->
  <form id="shop-form">
    <input type="text" id="shop-item" placeholder="Item Name" required disabled>
    <input type="submit" id="shop-add" value="Add" disabled>
  </form>
 
  <!-- (B) SHOPPING LIST -->
  <div id="shop-list"></div>
</div>

The HTML is very straightforward, and we only have 2 sections in the interface:

  1. <form id="shop-form"> – Just a simple, single text field HTML form that we will use to add items to the list.
  2. <div id="shop-list"> – We will use Javascript to dynamically generate the shopping list in this empty container.

That’s all to the HTML page, the rest of the mechanics will be driven by Javascript.

 

 

PART 2) SHOPPING LIST JAVASCRIPT

2A) SHOPPING LIST INITIALIZATION

2-shop-list.js
// (A) INITIALIZE SHOPPING LIST
items : [], // current shopping list
hform : null, // html add item <form>
hitem : null, // html add item <input> field
hadd : null, // html add item submit button
hlist : null, // html <div> shopping list
init : () => {
  // (A1) GET HTML ELEMENTS
  slist.hform = document.getElementById("shop-form");
  slist.hitem = document.getElementById("shop-item");
  slist.hadd = document.getElementById("shop-add");
  slist.hlist = document.getElementById("shop-list");
 
  // (A2) "ACTIVATE" HTML ADD ITEM FORM
  slist.hitem.setAttribute("autocomplete", "off");
  slist.hform.onsubmit = slist.add;
  slist.hitem.disabled = false;
  slist.hadd.disabled = false;
 
  // (A3) RESTORE PREVIOUS SHOPPING LIST
  if (localStorage.items == undefined) { localStorage.items = "[]"; }
  slist.items = JSON.parse(localStorage.items);
 
  // (A4) DRAW HTML SHOPPING LIST
  slist.draw();
}
window.addEventListener("load", slist.init);

The first thing that we do on page load is to run the slist.init() function to set up the shopping list. What this function does is very straightforward –

  • (A1 & A2) Get all the related HTML elements, and initialize the “add item” form.
  • (A3) Load the previously saved shopping cart from localstorage.items and restore it into slist.items.
  • (A4) Finally, draw the HTML shopping list.

Yep, that’s all. The only thing to take extra here is the slist.items array – This is where we save the raw shopping list data and use it to draw the HTML.

 

 

2B) ADD ITEM & SAVING INTO LOCAL STORAGE

2-shop-list.js
// (B) SAVE SHOPPING LIST INTO LOCAL STORAGE
save : () => {
  if (localStorage.items == undefined) { localStorage.items = "[]"; }
  localStorage.items = JSON.stringify(slist.items);
}
 
// (C) ADD NEW ITEM TO THE LIST
add : evt => {
  // (C1) PREVENT FORM SUBMIT
  evt.preventDefault();
 
  // (C2) ADD NEW ITEM TO LIST
  slist.items.push({
    name : slist.hitem.value, // item name
    done : false // true for "got it", false for "not yet"
  });
  slist.hitem.value = "";
  slist.save();
 
  // (C3) REDRAW HTML SHOPPING LIST
  slist.draw();
}

Moving on, the slist.add() function is fired whenever the user adds a new item.

  • (C1) Captain Obvious, to stop the HTML form from submitting and reloading the entire page.
  • (C2) Take note of how the new item is being pushed as an object into the slist.items array – { name:"ITEM NAME", done:true/false }
  • (B & C2) Update the localstorage.
  • (C3) Lastly, redraw the updated list.

 

2C) DELETE ITEM

2-shop-list.js
// (D) DELETE SELECTED ITEM
delete : id => { if (confirm("Remove this item?")) {
  slist.items.splice(id, 1);
  slist.save();
  slist.draw();
}}

Yep – This should be self-explanatory. Remove a specified item from slist.items, update the localstorage, then update the HTML.

 

 

2D) TOGGLE ITEM STATUS

2-shop-list.js
// (E) TOGGLE ITEM BETWEEN "GOT IT" OR "NOT YET"
toggle : id => {
  slist.items[id].done = !slist.items[id].done;
  slist.save();
  slist.draw();
}

This function toggles the “status” of the item between “got it” and “not yet” – Don’t think it needs explanation.

 

2E) DRAW HTML SHOPPING LIST

2-shop-list.js
// (F) DRAW THE HTML SHOPPING LIST
draw : () => {
  // (F1) RESET HTML LIST
  slist.hlist.innerHTML = "";
 
  // (F2) NO ITEMS
  if (slist.items.length == 0) {
    slist.hlist.innerHTML = "<div class='item-row item-name'>No items found.</div>";
  }
 
  // (F3) DRAW ITEMS
  else {
    for (let i in slist.items) {
      // ITEM ROW
      let row = document.createElement("div");
      row.className = "item-row";
      slist.hlist.appendChild(row);
 
      // ITEM NAME
      let name = document.createElement("div");
      name.innerHTML = slist.items[i].name;
      name.className = "item-name";
      if (slist.items[i].done) { name.classList.add("item-got"); }
      row.appendChild(name);
 
      // DELETE BUTTON
      let del = document.createElement("input");
      del.className = "item-del";
      del.type = "button";
      del.value = "Delete";;
      del.onclick = () => { slist.delete(i); };
      row.appendChild(del);
 
      // COMPLETED/NOT YET BUTTON
      let ok = document.createElement("input");
      ok.className = "item-ok";
      ok.type = "button";
      ok.value = slist.items[i].done ? "Not Yet" : "Got It";
      ok.onclick = () => { slist.toggle(i); };
      row.appendChild(ok);
    }
  }
}

Lastly, don’t let this slist.draw() function intimidate you. It may look massive, but all it does is loop through slist.hlist and create the HTML – Which we already went through in all of the above sections.

 

 

2F) GENERATED SHOPPING LIST HTML

<div class="item-row">
  <div class="item-name">1 bag of sugar</div>
  <input type="button" class="item-del" value="Delete">
  <input type="button" class="item-ok" value="Got It">
</div>

If you are wondering – This is an example of what the above slist.draw() generates into <div id="shop-list">.

 

PART 3) PROGRESSIVE WEB APP

3A) HTML META HEADERS

1-shop-list.html
<!-- WEB APP & ICONS -->
<link rel="icon" href="favicon.png" type="image/png">
<meta name="mobile-web-app-capable" content="yes">
<meta name="theme-color" content="white">
<link rel="apple-touch-icon" href="favicon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="JS Shopping List">
<meta name="msapplication-TileImage" content="favicon.png">
<meta name="msapplication-TileColor" content="#ffffff">

<!-- WEB APP MANIFEST -->
<!-- https://web.dev/add-manifest/ -->
<link rel="manifest" href="3-manifest.json">
 
<!-- SERVICE WORKER -->
<script>if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("3-worker.js");
}</script>

We already have a fully functioning app, and this part is completely optional. But to turn this into an installable web app, all we need is:

  • Define a whole load of “icons and app name” meta headers.
  • Create a web manifest file.
  • Register a service worker to support “offline mode”.

 

3B) WEB MANIFEST

3-manifest.json
{
  "short_name": "JS Shopping List",
  "name": "JS Shopping List",
  "icons": [{
    "src": "favicon.png",
    "sizes": "512x512",
    "type": "image/png"
  }],
  "start_url": "shop-list.html",
  "scope": "/",
  "background_color": "white",
  "theme_color": "white",
  "display": "standalone"
}

The manifest file is what it is. A file to contain information about your web app – The name, icons, preferred settings, etc..,

 

3C) SERVICE WORKER

3-worker.js
// (A) CREATE/INSTALL CACHE
self.addEventListener("install", evt => {
  self.skipWaiting();
  evt.waitUntil(
    caches.open("ShopList")
    .then(cache => cache.addAll([
      "favicon.png",
      "1-shop-list.css",
      "1-shop-list.html",
      "2-shop-list.js",
      "3-manifest.json"
    ]))
    .catch(err => console.error(err))
  );
});
 
// (B) CLAIM CONTROL INSTANTLY
self.addEventListener("activate", evt => self.clients.claim());
 
// (C) LOAD FROM CACHE FIRST, FALLBACK TO NETWORK IF NOT FOUND
self.addEventListener("fetch", evt => evt.respondWith(
  caches.match(evt.request).then(res => res || fetch(evt.request))
));

For those who are new, a service worker is simply a piece of Javascript that runs in the background. In this simple worker:

  • (A) We tell the browser to cache a whole list of project files.
  • (C) “Hijack” the fetch requests. If the requested file is cached, use the cached copy; If not, fallback to loading from the network.

In a nutshell, enabling “offline mode”. This app will still run from the browser cache even when the user is offline.

 

DOWNLOAD & NOTES

Here is the download link to the example code, so you don’t have to copy-paste everything.

 

SORRY FOR THE ADS...

But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.

Buy Me A Coffee Code Boxx eBooks

 

EXAMPLE CODE DOWNLOAD

Click here for the source code on GitHub gist, just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

EXTRA BITS & LINKS

That’s all for this project, and here is a small section on some extras and links that may be useful to you.

 

COMPATIBILITY CHECKS

This shopping list will work on all modern browsers.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

3 thoughts on “Simple Shopping List In Vanilla Javascript (Free Source Code)”

  1. This was very helpful, thank you!
    I noticed I am allowed to add spaces for the item and it adds a row with no item. I am trying to figure out how to add a statement that prevents a row to be added if the user enters just spaces, but I haven’t been able to figure that out. Any suggestions?
    Thanks again.

Comments are closed.