JavaScript increase button increases only once

浪子不回头ぞ 提交于 2020-06-17 10:02:47

问题


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 the basket[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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!