问题
I am a beginner in JS and working at a shopping cart. I have several products which are rendered in the page with ES6 template strings. Everything is working so far, you can add items to the basket and the basket and total update correctly. The only part I am having trouble with is the increase/decrease buttons: they only work once, if you click again the amount printed in the console stays the same.
I did find other SO post related to increment/decrement functions but the button keeps working only once, so I reckon the problem is related to something else in the code that I am overlooking.
Please see the code below:
this is the shopping cart that will be rendered
// select ul
const shoppingCart = document.querySelector('.cart-items');
// create a li item inside ul
let billContainer = document.createElement('li');
// attach an event listener to every li
billContainer.classList.add('list');
// create the markup for every item added to the cart
for(let j = 0; j < basket.length; j++){
const billMarkup = `
<p class="prodName">${basket[j].name}</p>
<div class="button-wrapper">
<button type="button" name="increase" class="increase">+</button>
<span class="quantity">${basket[j].quantity}</span>
<button type="button" name="decrease" class="decrease">-</button>
</div>
<p class="totPrice">£${basket[j].price}</p>
`;
// add the markup to the DOM
billContainer.innerHTML = billMarkup;
shoppingCart.appendChild(billContainer);
}
and this is the increase/decrease functionality (the event listener for the buttons is attached to their parent 'li'):
// attach an event listener to every li
const li = document.querySelectorAll('.list');
li.forEach( liItem => liItem.addEventListener('click', updateBill));
// add or remove items on click
function updateBill(e){
if(e.target.nodeName === 'BUTTON'){
// current value in the basket
let value = parseInt(this.querySelector('.quantity').textContent);
// if the user clicks on 'increase' button
if(e.target.name === 'increase'){
value++;
console.log(value);
// if the user clicks on 'decrease' button
} else if(e.target.name === 'decrease'){
value < 1 ? value = 1 : '';
value--;
console.log(value);
}
}
}
Thanks!
回答1:
Problem
Plus/minus buttons inc/decremented only once then wouldn't go any further.
Explanation
Once a value has changed, it is just a number in a variable floating in the console since that is the last statement that has anything to do with the value. So only the initial change is successful but when the buttons are clicked for the second time, the function goes back to span.quantity
and gets the value that's never been updated from the last click.
Solution
The easiest way to resolve the problem at hand is to update the value of span.quantity
:
if (e.target.name === 'increase') {
value++;
console.log(value);
} else if (e.target.name === 'decrease') {
value--;
value = value < 1 ? 1 : value;
console.log(value);
} else {
return false;
}
this.querySelector('.quantity').textContent = value;
Because you didn't provide a functional nor a copyable demo, I didn't bother to test it nor did I attempt to spot check your code. It's less effort to rewrite the source and resolve the problem and maybe prevent problems in the future.
Demo Highlights
The Demo uses a different API for referencing form controls and alternate methods and properties that are better versions of ones that are more commonly used. Event Delegation is used. Array methods might've been overkill but I like using them. Below are line item references to the Demo, unfortunately Stack Snippets don't have line numbers. The Plunker - index.html and README.md can be read together with line numbers.
HTMLFormControlsCollection
52 Declare the
<form>
,53 Referencing ALL form controls,
92-95 Create very short references to each form control,
96-99 Create references to their values and convert them to numbers,
102-103, 109-110 Simple and short expressions,
122 A grand total value
Template Literals
75-83 Improved the layout for the list items by using semantic elements. Each element is assigned a unique #id,
92-94 Flexible referencing of #ids originating from the results of 89 and 90.
Array Methods
90-91 By planning a specific naming strategy:
abc-0
, split('-').pop() returns the number end of an id and split('-').shift() returns the letters before the dash,113-120 Collecting all
.prc
; map() returns an array of price totals; reduce() returns the total sum;
Event Delegation/Event Object
52 Reference the
<form>
,54 Register the
<form>
to click events. This is the only EventListener needed, it will work for all of its children/descendants,88-91, 100 Reference origin of event with the
Event.target
property and not only determine the clicked element but others as well like siblings, parents/ancestors, and children/descendants.
Miscellaneous
56-71 It looks like the
basket
is an array of objects? Didn't see it in the OP so I had to guess. Removed thebasket[j].quantity
property because it makes more sense that each item is initially a quantity of 1.84 insertAdjacentHTML() is
innerHTML
on steroids.
Plunker
Demo
<!DOCTYPE html>
<html>
<head>
<style>
html,
body {
font: 400 16px/1.1 Consolas;
}
legend {
font-size: 1.3rem;
}
output,
input {
display: inline-block;
text-align: center;
}
[id^=qty] {
width: 1.5ch;
}
[id^=prc] {
min-width: 9ch;
}
[id^=prc]::before {
content: "= £";
}
[id^=bas]::before {
content: " x £";
}
#cart+label {
display: inline-block;
margin: 10px 0 0 40%;
}
#total::before {
content: " £";
}
</style>
</head>
<body>
<form id='cart'></form>
<label>Total:
<output id='total' form='cart'>0.00</output>
</label>
<script>
var cart = document.forms.cart;
var x = cart.elements;
cart.addEventListener('click', updateBill, false);
var basket = [{
name: "thing0",
price: 1.99
}, {
name: "thing1",
price: 12.99
}, {
name: "thing2",
price: 21.59
}, {
name: "thing3",
price: 0.09
}, {
name: "thing4",
price: 5.99
}];
for (let j = 0; j < basket.length; j++) {
var details = `
<fieldset id="item-${j}">
<legend>${basket[j].name}</legend>
<button id="inc-${j}" type="button">+</button>
<output id="qty-${j}">1</output>
<button id="dec-${j}" type="button">-</button>
<output id="bas-${j}">${basket[j].price}</output>
<output id="prc-${j}" class="prc">${basket[j].price}</output>
</fieldset>
`;
cart.insertAdjacentHTML('beforeend', details);
}
function updateBill(e) {
if (e.target.type === 'button') {
var ID = e.target.parentElement.id;
var idx = ID.split('-').pop();
var dir = e.target.id.split('-').shift();
var qty = x.namedItem(`qty-${idx}`);
var bas = x.namedItem(`bas-${idx}`);
var prc = x.namedItem(`prc-${idx}`);
var sum = x.total;
var quantity = parseInt(qty.value, 10);
var base = parseFloat(bas.value).toFixed(2);
var price = parseFloat(prc.value).toFixed(2);
var total = parseFloat(sum.value).toFixed(2);
if (dir === "inc") {
quantity++;
qty.value = quantity;
prc.value = quantity * base;
} else {
quantity--;
if (quantity <= 0) {
quantity = 1;
}
qty.value = quantity;
prc.value = quantity * base;
}
}
var prices = Array.from(document.querySelectorAll('.prc'));
var numbers = prices.map(function(dig, idx) {
return parseFloat(dig.value);
});
var grandTotal = numbers.reduce(function(acc, cur) {
return acc + cur;
}, 0);
x.total.value = grandTotal.toFixed(2);
}
</script>
</body>
</html>
来源:https://stackoverflow.com/questions/50086238/javascript-increase-button-increases-only-once