如何将产品项添加到购物车,以及如何在始终(重新)计算购物车项的总价的同时将其从购物车中删除?
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?
以下数组包含作为产品项目的对象。单击 Add to cart
按钮应通过 mytable
函数将相关项目呈现为 table 演示文稿,如下所示。
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}
];
这是一个循环,它应该根据上面提供的数据创建产品概览(包括事件处理)。
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>";
}
这是上述代码的其余部分,预计会将产品概述写入文档。还有上面提到的 mytable
函数,它应该创建一个项目购物车演示(包括事件处理)。
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>";
}
此外,我想处理购物车商品的移除。对于添加和删除商品的两种情况 to/from 我想计算所有购物车商品的总价的购物车。
如何才能做到这一点?
正在使用的技术...
网页Api
-
-
-
-
-
-
-
-
-
-
事件委托
- David Walsh :: How JavaScript Event Delegation Works
- MDN :: Introduction to events :: Event Delegation
语法
JavaScript Api / 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>
以下数组包含作为产品项目的对象。单击 Add to cart
按钮应通过 mytable
函数将相关项目呈现为 table 演示文稿,如下所示。
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}
];
这是一个循环,它应该根据上面提供的数据创建产品概览(包括事件处理)。
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>";
}
这是上述代码的其余部分,预计会将产品概述写入文档。还有上面提到的 mytable
函数,它应该创建一个项目购物车演示(包括事件处理)。
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>";
}
此外,我想处理购物车商品的移除。对于添加和删除商品的两种情况 to/from 我想计算所有购物车商品的总价的购物车。
如何才能做到这一点?
正在使用的技术...
网页Api
事件委托
- David Walsh :: How JavaScript Event Delegation Works
- MDN :: Introduction to events :: Event Delegation
语法
JavaScript Api / 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>