Welcome to a tutorial on how to create a simple vanilla Javascript Shopping Cart. Looking to build an eCommerce website or web app? Not too keen on the complicated bloated scripts and just need a simple interface? Well, here it is. A standalone local storage shopping cart that does not use any frameworks – Read on to find out!
TABLE OF CONTENTS
JAVASCRIPT SHOPPING CART
All right, let us now get into the mechanics of the shopping cart itself.
PART 1) THE HTML
<!-- (A) CART -->
<div id="cart-wrap">
<!-- (A1) PRODUCTS LIST -->
<div id="cart-products"></div>
<!-- (A2) CURRENT CART ITEMS -->
<div id="cart-items"></div>
</div>
<!-- (B) TEMPLATES -->
<!-- (B1) PRODUCT CELL -->
<template id="template-product">
<div class="p-item">
<img class="p-img">
<div class="p-info">
<div class="p-txt">
<div class="p-name"></div>
<div class="p-price"></div>
</div>
<button class="cart p-add">+</button>
</div>
</div>
</template>
<!-- (B2) CART ITEMS -->
<template id="template-cart">
<div class="c-item">
<input class="c-qty" type="number" min="0">
<div class="c-name"></div>
<button class="c-del cart">X</button>
</div>
</template>
<template id="template-cart-checkout">
<div class="c-go">
<button class="c-empty cart" onclick="cart.nuke()">Empty</button>
<button class="c-checkout cart" onclick="cart.checkout()">Checkout</button>
</div>
</template>
- The HTML interface should be very straightforward with only 2 sections in the
<div id="cart-wrap">
container:<div id="cart-products">
To contain the list of products.<div id="cart-items">
To show the current items in the cart.
- HTML templates for the product and cart items.
PART 2) THE PRODUCTS
// DUMMY PRODUCTS (PRODUCT ID : DATA)
var products = {
123: {
name : "iPon Min",
img : "JS-CART-1.png",
price : 123
},
124: {
name : "iPon Pro Max Plus",
img : "JS-CART-2.png",
price : 1234
}
};
Since we don’t have a database here, the only option is to hardcode all the products in a script.
PART 3) THE JAVASCRIPT
3A) THE PROPERTIES
var cart = {
// (A) PROPERTIES
hPdt : null, // html products list
hItems : null, // html current cart
items : {}, // current items in cart
iURL : "images/", // product image url folder
currency : "$", // currency symbol
total : 0, // total amount
};
Captain Obvious – The var cart
object will keep all the cart-related mechanics. Right at the top, we have a couple of properties.
cart.hPdt
andcart.hItems
shouldn’t be too difficult to understand – We will use these to refer to<div id="cart-products">
and<div id="cart-items">
.cart.items
contains the current items in the cart. This is simply an object that has the format ofPRODUCT ID : QUANTITY
.iURL
is the URL to the product image folder.currency
andtotal
The currency symbol and the total amount of items in the cart.
3B) WORKING WITH LOCALSTORAGE
// (B) LOCALSTORAGE CART
// (B1) SAVE CURRENT CART INTO LOCALSTORAGE
save : () => localStorage.setItem("cart", JSON.stringify(cart.items)),
// (B2) LOAD CART FROM LOCALSTORAGE
load : () => {
cart.items = localStorage.getItem("cart");
if (cart.items == null) { cart.items = {}; }
else { cart.items = JSON.parse(cart.items); }
},
// (B3) NUKE CART!
nuke : () => { if (confirm("Empty cart?")) {
cart.items = {};
localStorage.removeItem("cart");
cart.list();
}}
That’s right. Without a server session, whatever is in the cart will disappear as soon as the user reloads the page. To prevent that from happening, we will save the cart items into the localStorage
.
cart.save()
simply savescart.items
into the local storage.cart.load()
will fetch the data from the local storage and restorecart.items
.- Lastly,
cart.nuke()
will empty the entire cart.
3C) SHOPPING CART INITIALIZATION
// (C) INITIALIZE
init : () => {
// (C1) GET HTML ELEMENTS
cart.hPdt = document.getElementById("cart-products");
cart.hItems = document.getElementById("cart-items");
// (C2) DRAW PRODUCTS LIST
cart.hPdt.innerHTML = "";
let template = document.getElementById("template-product").content, p, item;
for (let id in cart.products) {
p = cart.products[id];
item = template.cloneNode(true);
item.querySelector(".p-img").src = cart.iURL + p.img;
item.querySelector(".p-name").textContent = p.name;
item.querySelector(".p-price").textContent = cart.currency + p.price.toFixed(2);
item.querySelector(".p-add").onclick = () => cart.add(id);
cart.hPdt.appendChild(item);
}
// (C3) LOAD CART FROM PREVIOUS SESSION
cart.load();
// (C4) LIST CURRENT CART ITEMS
cart.list();
}
The init()
function runs on page load. It may seem to be massive, but what it does is actually very basic:
- (C1) “Tie”
cart.hPdt
to<div id="cart-products">
andcart.hItems
to<div id="cart-items">
. - (C2) Draw the list of products.
- (C3) Load the cart from the previous session.
- (C4) Draw the current cart items.
3D) DRAW SHOPPING CART ITEMS
// (D) LIST CURRENT CART ITEMS (IN HTML)
list : () => {
// (D1) RESET
cart.total = 0;
cart.hItems.innerHTML = "";
let item, empty = true;
for (let key in cart.items) {
if (cart.items.hasOwnProperty(key)) { empty = false; break; }
}
// (D2) CART IS EMPTY
if (empty) {
item = document.createElement("div");
item.innerHTML = "Cart is empty";
cart.hItems.appendChild(item);
}
// (D3) CART IS NOT EMPTY - LIST ITEMS
else {
let template = document.getElementById("template-cart").content, p;
for (let id in cart.items) {
p = cart.products[id];
item = template.cloneNode(true);
item.querySelector(".c-del").onclick = () => cart.remove(id);
item.querySelector(".c-name").textContent = p.name;
item.querySelector(".c-qty").value = cart.items[id];
item.querySelector(".c-qty").onchange = function () { cart.change(id, this.value); };
cart.hItems.appendChild(item);
cart.total += cart.items[id] * p.price;
}
// (D3-3) TOTAL AMOUNT
item = document.createElement("div");
item.className = "c-total";
item.id = "c-total";
item.innerHTML = `TOTAL: ${cart.currency}${cart.total}`;
cart.hItems.appendChild(item);
// (D3-4) EMPTY & CHECKOUT
item = document.getElementById("template-cart-checkout").content.cloneNode(true);
cart.hItems.appendChild(item);
}
}
The list()
function draws the current cart items into <div id="cart-items">
. Looks complicated, but it is just generating a whole load of HTML.
3E) SHOPPING CART ACTIONS
// (E) ADD ITEM INTO CART
add : id => {
if (cart.items[id] == undefined) { cart.items[id] = 1; }
else { cart.items[id]++; }
cart.save(); cart.list();
},
// (F) CHANGE QUANTITY
change : (pid, qty) => {
// (F1) REMOVE ITEM
if (qty <= 0) {
delete cart.items[pid];
cart.save(); cart.list();
}
// (F2) UPDATE TOTAL ONLY
else {
cart.items[pid] = qty;
var total = 0;
for (let id in cart.items) {
cart.total += cart.items[id] * products[id].price;
document.getElementById("c-total").innerHTML = `TOTAL: ${cart.currency}${cart.total}`;
}
}
},
// (G) REMOVE ITEM FROM CART
remove : id => {
delete cart.items[id];
cart.save();
cart.list();
},
// (H) CHECKOUT
checkout : () => {
// SEND DATA TO SERVER
// CHECKS
// SEND AN EMAIL
// RECORD TO DATABASE
// PAYMENT
// WHATEVER IS REQUIRED
alert("TO DO");
/*
var data = new FormData();
data.append("cart", JSON.stringify(cart.items));
data.append("products", JSON.stringify(products));
data.append("total", cart.total);
fetch("SERVER-SCRIPT", { method:"POST", body:data })
.then(res=>res.text())
.then(res => console.log(res))
.catch(err => console.error(err));
*/
}
Finally, all of these functions deal with cart actions:
cart.add()
Adds a selected item into the cart. Remember the format ofcart.items = { ID : QUANTITY }
? That is exactly what we are doing here.cart.change()
Changes the quantity of the item in the cart.cart.remove()
Removes an item from the cart.cart.checkout()
Is up to you to complete – Send the order via email, SMS, save to database, save to Google Forms, etc…
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.
MULTIPLE PRODUCT CATEGORIES?
As you may have noticed, there are no product categories. But yes, it is possible to add sections with some changes:
- Create another
var categories
object to hold the product IDs of each category. For example{ CATEGORY-A: [123, 124, 125], CATEGORY-B: [1, 2, 3] }
. - Modify the
cart.draw()
function to draw the products in a selected category.
PRODUCT OPTIONS
For the guys who are sarcastically saying “adding product options should be easy for you, it can be done in 5 minutes” – Of course, it is “so simple”, even you can do it within “just a few minutes”. Feel free to challenge yourself:
- Create a new
var options = { color : [red, green, blue], size : [small, large], ... }
. - Attach the options to products.
- Change the product list to also draw the options.
- Change the way the cart records items –
PRODUCT-ID[OPTION][VALUE][OPTION][VALUE] : QUANTITY
.
Yes, that’s all, just a few steps only. Please don’t ask about product options again… It is “so simple” that I don’t even want to touch on it.
A WAY BETTER SHOPPING CART
A shopping cart with product options, categories, discount coupons, user system, admin panel, API, and an installable web app - Click here to check out my eBook!
HOW TO “COMPLETE THE CHECKOUT”
The only piece of advice I can give is to learn a server-side programming language of your choice – PHP, ASP, JSP, Python, or NodeJS. Everyone has different requirements.
- Save order to the database.
- Save order to Google Forms.
- Send order via email.
- Send order via SMS.
- Process online payment.
- Link up with other systems.
As you can guess, there is simply no way that I can explain “how to checkout” in every possible scenario and language. Neither can I give free consultation services. So it is up to you to complete your own checkout sequence. If you need more examples, check out the PHP MySQL shopping cart link below… Or get my eBook above. 😆
COMPATIBILITY CHECKS
- Local Storage – CanIUse
- Arrow Function – CanIUse
Works well in all modern browsers. If you want to be safe, use Modernizr to do feature checks and even load polyfills.
LINKS & REFERENCES
- Need a more “substantial” cart? Check out my PHP MySQL Shopping Cart tutorial.
- Shopping Cart Design Ideas – SpeckyBoy
- Javascript POS System – Code Boxx
- Javascript Food Ordering System – Code Boxx
- Example on CodePen – JS Shopping Cart
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!
Hi,
about previous posted message: here’s the use case:
step1: I load you cart page as is.
step 2: add items to cart => “Empty & checkout” div’s are displayed correctly
step 3: I edit the “products.js” , I just change the name and ID
—- file is now–
// DUMMY PRODUCTS (PRODUCT ID : DATA)
var products = {
333: {
name : “iPon MinAAAA”,
img : “JS-CART-1.png”,
price : 123
},
124: {
name : “iPon Pro Max Plus”,
img : “JS-CART-2.png”,
price : 1234
}
};
step 4: force reload of page (ctrl +F5)
=> In this case, this is where I have the BUG
STEP 1) Product ID 123, name : “IPON”.
STEP 2) Add product ID 123 to cart.
STEP 3) Change product ID 123 to 333.
STEP 4) JS cannot find PRODUCT 123.
Where do think the “BUG” is now? #USERERROR #FACEPALM 😆
P.S. Force reload will not remove the local storage, follow up with your own studies. If you are using a Chromium-based browser – Developer’s console > Application > Storage > Local storage
P.P.S Please don’t call this a “bug” when it is clearly working and your own modifications broke it. Good luck!
Hi
Hope you’re fine
Congratulations for this tutorial (just discovering you posts)
it seems there’s a bug in your code (codepen & downloaded code):
Uncaught TypeError: Cannot read properties of undefined (reading ‘name’)
at Object.list (cart.js:78:55)
at init (cart.js:51:10)
Also, seems (maybe because of this bug) that total and checkout div don’t display, even if cart is with items inside
Regards
Thanks for reporting, but I cannot replicate that error – It works as intended on Chrome, Edge, Opera, Firefox, Android Chrome.