-
-
Save mahmudalhakim/9720ded053c3856aa345048e981c1396 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment