Skip to content

Instantly share code, notes, and snippets.

@prof3ssorSt3v3
Last active December 7, 2022 16:15
Show Gist options
  • Save prof3ssorSt3v3/3e15d06a8128d6ca7deaa831a7a1e52b to your computer and use it in GitHub Desktop.
Save prof3ssorSt3v3/3e15d06a8128d6ca7deaa831a7a1e52b to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shopping Cart</title>
<meta name="viewport" content="width=device-width">
<style>
*{
padding: 0;
margin: 0;
}
html {
box-sizing: border-box;
font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Helvetica Neue", "Arial", sans-serif;
font-size: calc(1.5vh + 1vw + 1%);
line-height: 1.5;
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
*, *::before, *::after {
box-sizing: inherit;
}
body{
overflow: auto;
min-height: 100vh;
width: 100%;
}
main, header{
padding: 1rem 2rem;
}
p{
padding: 0.5rem 0;
}
main{
border-top: 5px solid #bada55;
display:flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
min-width: 600px;
}
#products{
display:grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 1rem;
flex:1 1 auto;
}
#cart{
flex: 1 1 20%;
}
.card{
border: 1px solid #999;
padding: 1rem;
box-sizing: content-box;
max-width: 200px;
position: relative;
}
.card img{
width: 100%;
filter: contrast(0.3);
}
.card .price{
position: absolute;
top: 3rem;
left: 2rem;
transform: rotate(30deg);
text-shadow: 2px 2px 2px #333, 1px 1px 2px #eee;
color: #bada55;
font-weight: 700;
font-size: 1.5rem;
}
.card h2{
font-size: 1.2rem;
}
.card p{
font-size: 0.8rem;
}
.card .btn{
border: 2px solid cornflowerblue;
padding: 0.25rem 1rem;
color: cornflowerblue;
cursor: pointer;
}
.cart-item{
border-bottom: 1px solid #666;
padding:1rem;
width: 100%;
position: relative;
}
.cart-item .price{
position: absolute;
top: 2rem;
bottom: 1rem;
right: 1rem;
width: 30%;
font-size: 0.8rem;
color: #AAA;
transform: rotate(30deg);
}
.cart-item .title{
font-size: 1rem;
color: #999;
line-height: 1.5rem;
height: 1.5rem;
width: 70%;
}
.cart-item .controls{
height: 1.5rem;
cursor: pointer;
width: 70%;
display: flex;
justify-content: space-between;
background-color: #eee;
}
.cart-item span:nth-child(1),
.cart-item span:nth-child(3){
border: 1px solid cornflowerblue;
color: cornflowerblue;
background: #fff;
font-size: 1rem;
line-height: 1.5rem;
padding: 0 0.5rem;
display: block;
}
</style>
</head>
<body>
<header>
<h1>My Store</h1>
</header>
<main>
<section id="products"></section>
<section id="cart"></section>
</main>
<script>
const CART = {
KEY: 'bkasjbdfkjasdkfjhaksdfjskd',
contents: [],
init(){
//check localStorage and initialize the contents of CART.contents
let _contents = localStorage.getItem(CART.KEY);
if(_contents){
CART.contents = JSON.parse(_contents);
}else{
//dummy test data
CART.contents = [
{id:1, title:'Apple', qty:5, itemPrice: 0.85},
{id:2, title:'Banana', qty:3, itemPrice: 0.35},
{id:3, title:'Cherry', qty:8, itemPrice: 0.05}
];
CART.sync();
}
},
async sync(){
let _cart = JSON.stringify(CART.contents);
await localStorage.setItem(CART.KEY, _cart);
},
find(id){
//find an item in the cart by it's id
let match = CART.contents.filter(item=>{
if(item.id == id)
return true;
});
if(match && match[0])
return match[0];
},
add(id){
//add a new item to the cart
//check that it is not in the cart already
if(CART.find(id)){
CART.increase(id, 1);
}else{
let arr = PRODUCTS.filter(product=>{
if(product.id == id){
return true;
}
});
if(arr && arr[0]){
let obj = {
id: arr[0].id,
title: arr[0].title,
qty: 1,
itemPrice: arr[0].price
};
CART.contents.push(obj);
//update localStorage
CART.sync();
}else{
//product id does not exist in products data
console.error('Invalid Product');
}
}
},
increase(id, qty=1){
//increase the quantity of an item in the cart
CART.contents = CART.contents.map(item=>{
if(item.id === id)
item.qty = item.qty + qty;
return item;
});
//update localStorage
CART.sync()
},
reduce(id, qty=1){
//reduce the quantity of an item in the cart
CART.contents = CART.contents.map(item=>{
if(item.id === id)
item.qty = item.qty - qty;
return item;
});
CART.contents.forEach(async item=>{
if(item.id === id && item.qty === 0)
await CART.remove(id);
});
//update localStorage
CART.sync()
},
remove(id){
//remove an item entirely from CART.contents based on its id
CART.contents = CART.contents.filter(item=>{
if(item.id !== id)
return true;
});
//update localStorage
CART.sync()
},
empty(){
//empty whole cart
CART.contents = [];
//update localStorage
CART.sync()
},
sort(field='title'){
//sort by field - title, price
//return a sorted shallow copy of the CART.contents array
let sorted = CART.contents.sort( (a, b)=>{
if(a[field] > b[field]){
return 1;
}else if(a[field] < a[field]){
return -1;
}else{
return 0;
}
});
return sorted;
//NO impact on localStorage
},
logContents(prefix){
console.log(prefix, CART.contents)
}
};
let PRODUCTS = [];
document.addEventListener('DOMContentLoaded', ()=>{
//when the page is ready
getProducts( showProducts, errorMessage );
//get the cart items from localStorage
CART.init();
//load the cart items
showCart();
});
function showCart(){
let cartSection = document.getElementById('cart');
cart.innerHTML = '';
let s = CART.sort('qty');
s.forEach( item =>{
let cartitem = document.createElement('div');
cartitem.className = 'cart-item';
let title = document.createElement('h3');
title.textContent = item.title;
title.className = 'title'
cartitem.appendChild(title);
let controls = document.createElement('div');
controls.className = 'controls';
cartitem.appendChild(controls);
let plus = document.createElement('span');
plus.textContent = '+';
plus.setAttribute('data-id', item.id)
controls.appendChild(plus);
plus.addEventListener('click', incrementCart)
let qty = document.createElement('span');
qty.textContent = item.qty;
controls.appendChild(qty);
let minus = document.createElement('span');
minus.textContent = '-';
minus.setAttribute('data-id', item.id)
controls.appendChild(minus);
minus.addEventListener('click', decrementCart)
let price = document.createElement('div');
price.className = 'price';
let cost = new Intl.NumberFormat('en-CA',
{style: 'currency', currency:'CAD'}).format(item.qty * item.itemPrice);
price.textContent = cost;
cartitem.appendChild(price);
cartSection.appendChild(cartitem);
})
}
function incrementCart(ev){
ev.preventDefault();
let id = parseInt(ev.target.getAttribute('data-id'));
CART.increase(id, 1);
let controls = ev.target.parentElement;
let qty = controls.querySelector('span:nth-child(2)');
let item = CART.find(id);
if(item){
qty.textContent = item.qty;
}else{
document.getElementById('cart').removeChild(controls.parentElement);
}
}
function decrementCart(ev){
ev.preventDefault();
let id = parseInt(ev.target.getAttribute('data-id'));
CART.reduce(id, 1);
let controls = ev.target.parentElement;
let qty = controls.querySelector('span:nth-child(2)');
let item = CART.find(id);
if(item){
qty.textContent = item.qty;
}else{
document.getElementById('cart').removeChild(controls.parentElement);
}
}
function getProducts(success, failure){
//request the list of products from the "server"
const URL = "https://prof3ssorst3v3.github.io/media-sample-files/products.json?";
fetch(URL, {
method: 'GET',
mode: 'cors'
})
.then(response=>response.json())
.then(showProducts)
.catch(err=>{
errorMessage(err.message);
});
}
function showProducts( products ){
PRODUCTS = products;
//take data.products and display inside <section id="products">
let imgPath = '../video-pages/img/';
let productSection = document.getElementById('products');
productSection.innerHTML = "";
products.forEach(product=>{
let card = document.createElement('div');
card.className = 'card';
//add the image to the card
let img = document.createElement('img');
img.alt = product.title;
img.src = imgPath + product.img;
card.appendChild(img);
//add the price
let price = document.createElement('p');
let cost = new Intl.NumberFormat('en-CA',
{style:'currency', currency:'CAD'}).format(product.price);
price.textContent = cost;
price.className = 'price';
card.appendChild(price);
//add the title to the card
let title = document.createElement('h2');
title.textContent = product.title;
card.appendChild(title);
//add the description to the card
let desc = document.createElement('p');
desc.textContent = product.desc;
card.appendChild(desc);
//add the button to the card
let btn = document.createElement('button');
btn.className = 'btn';
btn.textContent = 'Add Item';
btn.setAttribute('data-id', product.id);
btn.addEventListener('click', addItem);
card.appendChild(btn);
//add the card to the section
productSection.appendChild(card);
})
}
function addItem(ev){
ev.preventDefault();
let id = parseInt(ev.target.getAttribute('data-id'));
console.log('add to cart item', id);
CART.add(id, 1);
showCart();
}
function errorMessage(err){
//display the error message to the user
console.error(err);
}
</script>
</body>
</html>
@CHEN-YI-TING
Copy link

thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment