简体   繁体   中英

How does one add a product-item to a shopping-cart and how would one remove it from there while always (re)calculating the cart items' total price?

The following array features objects which are the product items. Clicking at the Add to cart button should render the related item into a table presentation via the mytable function as shown further below.

var product = [
  {"name":"jeans","image":"pics/jeans3.jpg","price":500},
  {"name":"hoodie","image":"pics/hoodie.jpg","price":700},
  {"name":"shirt","image":"pics/shirt.jpg","price":450},
  {"name":"sweter","image":"pics/sweter.jpg","price":1100},
  {"name":"trouser","image":"pics/trouser.jpg","price":600},
  {"name":"tshirt","image":"pics/tshirt.jpg","price":250}
];

Here is the loop which is supposed to create the product overview (event handling inclusive) from the above provided data.

var head = "<div id='main'>";

for (var i in product) {
  head += "<div class='pro'>";
  head += "<h1>" + product[i].name + "</h1>";
  head += "<img src=" + product[i].image + ">";
  head += "<p>" + product[i].price + "</p>";
  head += "<button onclick='mytable(i)'>Add to cart</button>"
  head += "</div>";
}

And here is the rest of the above code which is expected to write the product overview to the document. There is also the above mentioned mytable function which is supposed to create an items shopping cart presentation (event handling inclusive).

head += "</div>";
head += "<div id='cart'> </div>"
document.write(head);

function mytable(i) {
     document.getElementById('cart').innerHTML = "<table border='1'> <tr><th>Product name</th>  <th>Quantity</th>  <th>Price</th> <th>image</th><th><button>Remove Items</button></th></tr></table>";
} 

Additionally I want to handle the removal of a cart item. For both cases adding and removing items to/from the cart I want to calculate the total price of all cart items.

How could one achieve this?

Techniques being used ...

Web Api

Event delegation

Syntax

Expressions and operators

JavaScript Api / Methods of Standard built-in objects

 const productList = [ { pid: "abcd-0987-WXYZ", name: "jeans", price: 500, image: "https://media.istockphoto.com/photos/blue-denim-picture-id501250332" }, { pid: "efgh-1234-QRST", name: "hoodie", price: 700, image: "https://media.istockphoto.com/photos/faceless-man-in-hoodie-standing-isolated-on-black-picture-id916306960" }, { pid: "ijkl-6543-MNOP", name: "shirt", price: 450, image: "https://media.istockphoto.com/photos/men-shirt-for-clothing-isolated-on-white-background-picture-id641319368" }, { pid: "mnop-5678-IJKL", name: "sweater", price: 1100, image: "https://media.istockphoto.com/photos/minimalistic-rustic-composition-with-stacked-vintage-knitted-easy-picture-id1049751604" }, { pid: "qrst-2109-EFGH", name: "trouser", price: 600, image: "https://media.istockphoto.com/photos/pants-picture-id168790494" }, { pid: "wxyz-9012-ABCD", name: "tshirt", price: 250, image: "https://media.istockphoto.com/photos/close-up-of-colorful-tshirts-on-hangers-apparel-background-picture-id1170635789" }, ]; function createElementFromMarkup(html) { const renderBox = document.createElement('div'); renderBox.innerHTML = html; return renderBox.firstElementChild; } function createItemMainView(data) { return createElementFromMarkup(` <li data-pid="${ data.pid }"> <h3>${ data.name }</h3> <img src="${ data.image }"/> <dl><dt>Price</dt><dd>${ data.price }</dd></dl> <button data-add-pid="${ data.pid }" data-text="Add to cart">Add to cart</button> </li> `); } function createItemCartView(data) { return createElementFromMarkup(` <li data-pid="${ data.pid }"> <h3>${ data.name }</h3> <dl><dt>Price</dt><dd>${ data.price }</dd></dl> <button data-remove-pid="${ data.pid }">Remove</button> </li> `); } function createShoppingItem(data) { return { data, view: { main: createItemMainView(data), cart: createItemCartView(data), }, checkout: { isInCart: false, orderCount: 0, }, }; } function scrollIntoViewIfNeeded(elmNode) { if (elmNode) { const whichScrollIntoView = elmNode.scrollIntoViewIfNeeded ? 'scrollIntoViewIfNeeded' : 'scrollIntoView'; elmNode[whichScrollIntoView](); } } function updateShoppingCartTotal(elmCartTotal, shoppingState) { const total = Object .values(shoppingState) .reduce((sum, item) => (sum + ((item.data.price ?? 0) * (item.checkout.orderCount ?? 0))), 0 ); elmCartTotal.textContent = (total === 0) ? '' : total; } function updateCartItemPriceView(elmPrice, price, orderCount) { elmPrice.textContent = (orderCount >= 2) ? `${ price } x ${ orderCount }` : price; } function updateAddButtonItemCount(elmButton, orderCount) { const { text: buttonText } = elmButton.dataset; elmButton.textContent = (orderCount >= 1) ? `${ buttonText } (${ orderCount })` : buttonText; } function updateOrderCounts(pid, context) { const { target: { elmMainOverview, elmCartOverview, elmCartTotal }, state: shoppingState, } = context; const shoppingItem = shoppingState[pid]; const orderCount = shoppingItem?.checkout?.orderCount ?? 0; const elmButton = elmMainOverview .querySelector(`[data-add-pid="${ pid }"]`); const elmPrice = (orderCount >= 1) && elmCartOverview .querySelector(`[data-pid="${ pid }"] dd`); if (elmButton) { updateAddButtonItemCount(elmButton, orderCount); } if (elmPrice) { updateCartItemPriceView(elmPrice, shoppingItem?.data?.price, orderCount); } updateShoppingCartTotal(elmCartTotal, shoppingState); } function handleAddToCartWithBoundTargetAndState(evt) { const target = evt.target.closest('[data-add-pid]'); if (target) { const { addPid: pid } = target.dataset; const { target: { elmCartOverview }, state: shoppingState, } = this; const item = shoppingState[pid]; if (item) { if (item.checkout.isInCart === false) { elmCartOverview.appendChild(item.view.cart.cloneNode(true)); item.checkout.isInCart = true; } item.checkout.orderCount += 1; scrollIntoViewIfNeeded( elmCartOverview.querySelector(`[data-pid="${ pid }"]`) ); updateOrderCounts(pid, this); } // console.log('Add To Cart :: pid ...', pid); } console.log('Add To Cart :: evt.target ...', evt.target); } function handleRemoveFromCartWithBoundTargetAndState(evt) { const target = evt.target.closest('[data-remove-pid]'); if (target) { const { removePid: pid } = target.dataset; const { target: { elmMainOverview, elmCartOverview }, state: shoppingState, } = this; const item = shoppingState[pid]; if (item) { const selector = `[data-pid="${ pid }"]`; scrollIntoViewIfNeeded(elmMainOverview.querySelector(selector)); elmCartOverview.querySelector(selector)?.remove(); elmMainOverview .querySelector(`[data-add-pid="${ pid }"]`).focus?.(); item.checkout.isInCart = false; item.checkout.orderCount = 0; updateOrderCounts(pid, this); } // console.log('Remove From Cart :: pid ...', pid); } console.log('Remove From Cart :: evt.target ...', evt.target); } function main() { const shoppingState = productList .map(createShoppingItem) .reduce((state, item) => Object.assign(state, { [item.data.pid]: item }), Object.create(null) ); console.log({ shoppingState }) const elmMainOverview = document .querySelector('[data-product-overview]'); const elmShoppingCart = document .querySelector('[data-shopping-cart]'); const elmCartOverview = elmShoppingCart ?.querySelector('[data-cart-overview]'); const elmCartTotal = elmShoppingCart ?.querySelector('[data-cart-total]'); const handlerContext = { target: { elmMainOverview, elmCartOverview, elmCartTotal, }, state: shoppingState, }; elmMainOverview.addEventListener('click', handleAddToCartWithBoundTargetAndState.bind(handlerContext) ); elmCartOverview.addEventListener('click', handleRemoveFromCartWithBoundTargetAndState.bind(handlerContext) ); // initially render product list from shopping state. Object .values(shoppingState) .forEach(item => elmMainOverview.appendChild(item.view.main.cloneNode(true)) ); } main();
 * { margin: 0; padding: 0; } ul, li { list-style: none; } li { position: relative; margin-bottom: 5px; padding: 5px; } li:hover { background-color: #eee; } h3, dl, button { font-size: 12px; } img { max-height: 54px; max-width: 72px; } dl::after { clear: left; } dl dt { float: left; } dl dd::before { content: ': '; } button { position: absolute; right: 5px; bottom: 5px; } button:hover { cursor: pointer; } button:target, button:focus, button:focus-within { outline: 1px solid #06f; } main { position: relative; max-width: 25%; } #mini-cart { position: fixed; right: 60%; top: 0; min-width: 14%; height: 100%; overflow-y: scroll; font-size: 12px; } #mini-cart button { position: unset; } .as-console-wrapper { min-height: 100%!important; width: 60%; top: 0; left: auto!important; right: 0; }
 <main> <ul data-product-overview> </ul> </main> <section id="mini-cart" data-shopping-cart> <a href="#mini-cart"> Mini Cart <output data-cart-total></output> </a> <ul data-cart-overview> </ul> </section>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM