Creating a shopping cart using HTML, CSS, and JavaScript involves setting up a user interface for displaying products, adding products to the cart, viewing the cart, and removing products from the cart. Here’s a step-by-step guide to building a basic shopping cart:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Online Store</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<nav>
<div class="container">
<strong class="logo">Online Store</strong>
<button class="cart-btn">
<i class="bi bi-cart"></i>
<small class="cart-qty">0</small>
</button>
</div>
</nav>
<main>
<div class="container">
<div class="products"></div>
</div>
</main>
<div class="cart-overlay"></div>
<div class="cart">
<div class="cart-header">
<i class="bi bi-arrow-right cart-close"></i>
<h2>Your Cart</h2>
</div>
<div class="cart-body"></div>
<div class="cart-footer">
<div>
<strong>Total</strong>
<span class="cart-total">0</span>
</div>
<button class="cart-clear">Clear Cart</button>
<button class="checkout">Checkout</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
CSS (styles.css)
@import url("https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600&display=swap");
:root {
--color1: #ab4e68;
--color2: #b07156;
--color3: #533745;
--color4: #9d9171;
--color5: #c4a287;
--color6: #4a646c;
--color7: #333;
--color8: #fff;
--transition: all 0.3s ease-in-out;
}
* {
font-family: "Jost", sans-serif;
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
display: grid;
grid-template-rows: auto 1fr;
gap: 30px;
min-height: 100vh;
}
/* prevent body scroll when cart is open */
body:has(.show) {
overflow: hidden;
}
button {
cursor: pointer;
border: none;
border-radius: 3px;
padding: 3px 10px;
transition: var(--transition);
}
img {
width: 100%;
height: auto;
display: block;
}
nav {
background: var(--color1);
color: var(--color8);
padding-block: 20px;
}
nav > .container {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
text-transform: uppercase;
}
.cart-btn {
padding: 3px 8px;
background: transparent;
color: inherit;
position: relative;
}
.cart-btn .bi {
font-size: 1.5rem;
}
.cart-btn:hover {
background: var(--color5);
}
.cart-qty {
position: absolute;
top: 0;
right: 0;
transform: translate(50%, -50%);
background: var(--color7);
padding: 0 5px;
border-radius: 3px;
display: none;
}
.cart-qty.visible {
display: block;
}
.container {
max-width: 1200px;
width: 90%;
margin: auto;
}
.products {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.product {
text-align: center;
position: relative;
}
.product img {
height: 250px;
object-fit: contain;
}
.product:hover img {
opacity: 0.75;
}
.product h3 {
margin-top: 10px;
color: var(--color4);
font-size: 1rem;
}
.product h5 {
margin-top: 5px;
color: var(--color6);
}
.product button {
position: absolute;
top: 10px;
right: 10px;
background: var(--color1);
color: var(--color8);
padding: 5px 10px;
font-size: 1rem;
display: flex;
align-items: center;
gap: 5px;
opacity: 0;
}
.product:hover button {
opacity: 1;
}
.product button::before {
font-family: "bootstrap-icons";
font-size: 1.5rem;
content: "\F23F";
}
.product button:disabled::before {
content: "\F23A";
}
.product button:hover {
background: var(--color2);
}
.product button:disabled {
background: var(--color3);
filter: brightness(1.75);
}
/* cart */
.cart-overlay {
position: fixed;
inset: 0;
opacity: 0.5;
visibility: hidden;
cursor: pointer;
background: var(--color7);
transition: var(--transition);
}
.cart-overlay.show {
visibility: visible;
}
.cart {
position: fixed;
inset-block: 0;
right: 0;
width: 100%;
max-width: 420px;
padding: 20px;
display: grid;
grid-template-rows: auto 1fr auto;
transform: translateX(100%);
transition: var(--transition);
background: var(--color8);
}
.cart.show {
transform: none;
}
.cart-header {
position: relative;
text-align: center;
padding-block: 10px;
border-bottom: 1px solid #ddd;
}
.cart-close {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
font-size: 1.5rem;
cursor: pointer;
}
.cart-body {
display: grid;
align-content: start;
gap: 20px;
padding-block: 20px;
overflow: auto;
}
/* hide footer if the cart is empty */
.cart-body:has(.cart-empty) + .cart-footer {
display: none;
}
.cart-empty {
text-align: center;
padding-block: 20px;
font-size: 1.25rem;
color: var(--color6);
}
.cart-item {
display: flex;
gap: 10px;
}
.cart-item img {
width: 100px;
height: 100px;
object-fit: contain;
}
.cart-item-detail {
display: flex;
flex-direction: column;
flex: 1;
}
.cart-item-detail h3 {
font-size: 1rem;
color: var(--color4);
}
.cart-item-detail h5 {
color: var(--color6);
}
.cart-item-amount {
margin-top: auto;
display: flex;
align-items: center;
gap: 10px;
font-size: 1.25rem;
}
.cart-item-amount .bi {
cursor: pointer;
opacity: 0.25;
}
.cart-item-amount .bi:hover {
opacity: 1;
}
.cart-item-amount .qty {
width: 2rem;
text-align: center;
}
.cart-item-price {
margin-left: auto;
}
.cart-footer {
border-top: 1px solid #ddd;
padding-block: 10px;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.cart-footer div {
grid-column: 1 / -1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 1.25rem;
}
.cart-footer strong {
font-weight: 500;
color: var(--color4);
}
.cart-footer button {
padding-block: 10px;
text-transform: uppercase;
}
.cart-clear {
background: var(--color7);
color: var(--color8);
}
.cart-clear:hover {
filter: brightness(1.75);
}
.checkout {
background: var(--color1);
color: var(--color8);
}
.checkout:hover {
background: var(--color2);
}
JavaScript (script.js)
let products = [];
let cart = [];
//* selectors
const selectors = {
products: document.querySelector(".products"),
cartBtn: document.querySelector(".cart-btn"),
cartQty: document.querySelector(".cart-qty"),
cartClose: document.querySelector(".cart-close"),
cart: document.querySelector(".cart"),
cartOverlay: document.querySelector(".cart-overlay"),
cartClear: document.querySelector(".cart-clear"),
cartBody: document.querySelector(".cart-body"),
cartTotal: document.querySelector(".cart-total"),
};
//* event listeners
const setupListeners = () => {
document.addEventListener("DOMContentLoaded", initStore);
// product event
selectors.products.addEventListener("click", addToCart);
// cart events
selectors.cartBtn.addEventListener("click", showCart);
selectors.cartOverlay.addEventListener("click", hideCart);
selectors.cartClose.addEventListener("click", hideCart);
selectors.cartBody.addEventListener("click", updateCart);
selectors.cartClear.addEventListener("click", clearCart);
};
//* event handlers
const initStore = () => {
loadCart();
loadProducts("https://fakestoreapi.com/products")
.then(renderProducts)
.finally(renderCart);
};
const showCart = () => {
selectors.cart.classList.add("show");
selectors.cartOverlay.classList.add("show");
};
const hideCart = () => {
selectors.cart.classList.remove("show");
selectors.cartOverlay.classList.remove("show");
};
const clearCart = () => {
cart = [];
saveCart();
renderCart();
renderProducts();
setTimeout(hideCart, 500);
};
const addToCart = (e) => {
if (e.target.hasAttribute("data-id")) {
const id = parseInt(e.target.dataset.id);
const inCart = cart.find((x) => x.id === id);
if (inCart) {
alert("Item is already in cart.");
return;
}
cart.push({ id, qty: 1 });
saveCart();
renderProducts();
renderCart();
showCart();
}
};
const removeFromCart = (id) => {
cart = cart.filter((x) => x.id !== id);
// if the last item is remove, close the cart
cart.length === 0 && setTimeout(hideCart, 500);
renderProducts();
};
const increaseQty = (id) => {
const item = cart.find((x) => x.id === id);
if (!item) return;
item.qty++;
};
const decreaseQty = (id) => {
const item = cart.find((x) => x.id === id);
if (!item) return;
item.qty--;
if (item.qty === 0) removeFromCart(id);
};
const updateCart = (e) => {
if (e.target.hasAttribute("data-btn")) {
const cartItem = e.target.closest(".cart-item");
const id = parseInt(cartItem.dataset.id);
const btn = e.target.dataset.btn;
btn === "incr" && increaseQty(id);
btn === "decr" && decreaseQty(id);
saveCart();
renderCart();
}
};
const saveCart = () => {
localStorage.setItem("online-store", JSON.stringify(cart));
};
const loadCart = () => {
cart = JSON.parse(localStorage.getItem("online-store")) || [];
};
//* render functions
const renderCart = () => {
// show cart qty in navbar
const cartQty = cart.reduce((sum, item) => {
return sum + item.qty;
}, 0);
selectors.cartQty.textContent = cartQty;
selectors.cartQty.classList.toggle("visible", cartQty);
// show cart total
selectors.cartTotal.textContent = calculateTotal().format();
// show empty cart
if (cart.length === 0) {
selectors.cartBody.innerHTML =
'<div class="cart-empty">Your cart is empty.</div>';
return;
}
// show cart items
selectors.cartBody.innerHTML = cart
.map(({ id, qty }) => {
// get product info of each cart item
const product = products.find((x) => x.id === id);
const { title, image, price } = product;
const amount = price * qty;
return `
<div class="cart-item" data-id="${id}">
<img src="${image}" alt="${title}" />
<div class="cart-item-detail">
<h3>${title}</h3>
<h5>${price.format()}</h5>
<div class="cart-item-amount">
<i class="bi bi-dash-lg" data-btn="decr"></i>
<span class="qty">${qty}</span>
<i class="bi bi-plus-lg" data-btn="incr"></i>
<span class="cart-item-price">
${amount.format()}
</span>
</div>
</div>
</div>`;
})
.join("");
};
const renderProducts = () => {
selectors.products.innerHTML = products
.map((product) => {
const { id, title, image, price } = product;
// check if product is already in cart
const inCart = cart.find((x) => x.id === id);
// make the add to cart button disabled if already in cart
const disabled = inCart ? "disabled" : "";
// change the text if already in cart
const text = inCart ? "Added in Cart" : "Add to Cart";
return `
<div class="product">
<img src="${image}" alt="${title}" />
<h3>${title}</h3>
<h5>${price.format()}</h5>
<button ${disabled} data-id=${id}>${text}</button>
</div>
`;
})
.join("");
};
//* api functions
const loadProducts = async (apiURL) => {
try {
const response = await fetch(apiURL);
if (!response.ok) {
throw new Error(`http error! status=${response.status}`);
}
products = await response.json();
console.log(products);
} catch (error) {
console.error("fetch error:", error);
}
};
//* helper functions
const calculateTotal = () => {
return cart
.map(({ id, qty }) => {
const { price } = products.find((x) => x.id === id);
return qty * price;
})
.reduce((sum, number) => {
return sum + number;
}, 0);
};
Number.prototype.format = function () {
return this.toLocaleString("en-US", {
style: "currency",
currency: "USD",
});
};
//* initialize
setupListeners();
Explanation
- HTML Structure:
- The HTML includes containers for products and the shopping cart, along with a heading and a total price display.
- CSS Styling:
- The CSS styles the container, products, and cart items for a clean, modern look.
- Products and cart items are styled with a border and some padding.
- Buttons are styled for a consistent look with hover effects.
- JavaScript Functionality:
- The JavaScript initializes an array of products and an empty cart array.
- Functions to display products, add to cart, remove from cart, and update the total price are defined.
- Event listeners are set up for adding and removing items from the cart.
- The
displayProductsfunction creates HTML elements for each product and adds them to the products container. - The
displayCartfunction creates HTML elements for each cart item and adds them to the cart container, and also updates the total price. - The
addToCartfunction adds a product to the cart array and updates the display. - The
removeFromCartfunction removes a product from the cart array and updates the display. - The
updateTotalfunction calculates the total price of items in the cart and updates the total display.
This setup provides a simple and functional shopping cart. You can expand it by adding more features like quantity selection, saving the cart to local storage, or integrating with a backend server for persistent data storage.


+91 7905834592
Enquiry Now
piyushmnm@gmail.com
piyush.gupta384
Reviews
There are no reviews yet. Be the first one to write one.