Skip to content
Advertisement

how to make multiple counter buttons not interfere with each other

So basically I am making an online restaurant website where you can order food. I am going to make cards for each food item listed. Right now, I am making buttons that add and subtract the number of each item the customer wants to purchase.

var counter = document.querySelector(".count");
var currentNumber = 0;
var classicBurger = 5.18;
var bargeBurger = 6.13;

function addOne(price) {
  currentNumber += 1;
  counter.innerHTML = currentNumber;
  document.querySelector(".total-price").innerHTML =
    "$" + (price * currentNumber).toFixed(2);
}

function subtractOne(price) {
  if (currentNumber > 0) {
    currentNumber += -1;
    counter.innerHTML = currentNumber;
    document.querySelector(".total-price").innerHTML =
      "$" + (price * currentNumber).toFixed(2);
  } else {
    counter.innerHTML = 0;
  }
}
   <html>
     <div class="food-item">
        <h3 class="item-title">Classic Burger</h3>
        <p class="item-description">
          Classic beef patty with lettuce, tomato, and cheese.
        </p>

        <div class="add-subtract-items">
          <button class="subtract-item" onclick="subtractOne(classicBurger)">
            -
          </button>
          <span class="count">0</span>
          <button class="add-item" onclick="addOne(classicBurger)">+</button>
        </div>

        <p class="total-price">$0.00</p>
      </div>

      <div class="food-item">
        <h3 class="item-title">Barge Burger</h3>
        <p class="item-description">
          Classic beef patty with lettuce, tomato, and cheese.
        </p>

        <div class="add-subtract-items">
          <button class="subtract-item" onclick="subtractOne(bargeBurger)">
            -
          </button>
          <span class="count">0</span>
          <button class="add-item" onclick="addOne(bargeBurger)">+</button>
        </div>

        <p class="total-price">$0.00</p>
      </div>
      <html/>

How do I get the second button to effect the correct HTML rather than the total of the first item?

Advertisement

Answer

The problem with your code is the point at which you declare your variables, as well as the means by which you select the elements:

// here we get the first element in the document:
var counter = document.querySelector(".count");
// we set the currentNumber (the 'count') variable to 0:
var currentNumber = 0;

/* ... */

function addOne(price) {
  // regardless of which button, for which menu-item,
  // is pressed we increase the count (but this variable
  // is applied as the counter for *all* menu-items):
  currentNumber += 1;

  // here we're using the first '.count' element in the
  // document (regardless of which menu-item we're
  // trying to order, or remove from the order):
  counter.innerHTML = currentNumber;

  // and again, we're using the first element within the
  // document (regardless of which menu-item the buttons
  // relate to):
  document.querySelector(".total-price").innerHTML =
    "$" + (price * currentNumber).toFixed(2);
}

// the same is true, below, for the subtraction function
// which I've removed for brevity

Instead, we need to look at which <button> was pressed and from there find the correct menu-item to increment or decrement; so in the following code I’ve taken advantage of EventTarget.addEventListener() to pass a reference to the triggered Event to the function that is bound as the event-listener:

// declaring named functions using Arrow functions (since we're not using the
// 'this' variable).
// a named function to format the cost to two decimal places:
const formatCost = (cost) => {
    return (Math.ceil(cost * 100) / 100).toFixed(2);
  },
  // the following functions take advantage of EventTarget.addEventListener()
  // passing the event-object to the function that's bound as the event-listener:
  addOne = (evt) => {
    // from the event-object we first retrieve the 'evt.currentTarget', this
    // is the element to which the event-listener was bound (in this case the
    // <button class="add-item"> elements), from there we use Element.closest()
    // to find the ancestor <div class="food-item"> element (effectively to find
    // the first ancestor that wraps the <button> elements as well as the other
    // elements we wish to use):
    let cardParent = evt.currentTarget.closest('div.food-item'),
      // from the cardParent we then use Element.querySelector() to select
      // the first element within that Element (the cardParent) that matches
      // the supplied CSS selector:
      costElement = cardParent.querySelector('span.cost'),
      countElement = cardParent.querySelector('span.count'),

      // from those elements we determine the cost, using parseFloat()
      // to convert the text-content of the element into a number we
      // can work with:
      cost = parseFloat(costElement.textContent),
      // we update the current count by adding 1 to the number
      // retrieved with parseInt():
      count = parseInt(countElement.textContent) + 1;

    // we then update the countElement to reflect the new - increased - count:
    countElement.textContent = count;

    // and we then find the '.total-price' element, and update its text-content
    // to reflect the formatted cost:
    cardParent.querySelector('.total-price').textContent = formatCost(cost * count);
  },
  subtractOne = (evt) => {
    // here we do almost exactly as we did above:
    let cardParent = evt.currentTarget.closest('div.food-item'),
      costElement = cardParent.querySelector('span.cost'),
      countElement = cardParent.querySelector('span.count'),
      cost = parseFloat(costElement.textContent),
      // obviously we don't (yet) adjust the count:
      count = parseInt(countElement.textContent);

    // because we first need to check that the count is greater than zero:
    if (count > 0) {
      // if so, we then subtract one from the count:
      count = count - 1;
      // we then update the countElement to reflec the new count:
      countElement.textContent = count;
    }

    // and finally we update the '.total-price' element to reflect the new cost:
    cardParent.querySelector('.total-price').textContent = formatCost(cost * count);
  };

// here we select all elements in the document that match the supplied CSS selector, and
// use NodeList.forEach() to iterate over those elements, and:
document.querySelectorAll('button.subtract-item').forEach(
  // we then bind the subtractOne() function (note the lack of parentheses)
  // as the 'click' handler for the current element of the NodeList:
  (subtraction) => subtraction.addEventListener('click', subtractOne)
);
// as above, but obviously we're binding the addOne() function
// to the 'button.add-item' elements:
document.querySelectorAll('button.add-item').forEach(
  (addition) => addition.addEventListener('click', addOne)
);
*,
 ::before,
 ::after {
  box-sizing: border-box;
  font: normal 400 1rem / 1.5 sans-serif;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  flex-wrap: wrap;
  gap: 1em;
  padding: 1em;
}

.food-item {
  border: 1px solid #000;
  border-radius: 1em;
  padding: 0.5em;
}

h3 {
  font-weight: 600;
}

.add-subtract-items {
  align-content: center;
  display: flex;
  gap: 0.5em;
  justify-content: center;
  width: minmax(6em, 40%);
}

button,
span.count {
  text-align: center;
}

button {
  cursor: pointer;
  width: 2em;
}

:is(.cost, .total-price)::before {
  content: "$";
}
<div class="food-item">
  <h3 class="item-title">Classic Burger</h3>
  <span class="cost">5.18</span>
  <p class="item-description">
    Classic beef patty with lettuce, tomato, and cheese.
  </p>

  <div class="add-subtract-items">
    <button class="subtract-item">
      -
    </button>
    <span class="count">0</span>
    <button class="add-item">+</button>
  </div>

  <p class="total-price">0.00</p>
</div>

<div class="food-item">
  <h3 class="item-title">Barge Burger</h3>
  <span class="cost">6.13</span>
  <p class="item-description">
    Classic beef patty with lettuce, tomato, and cheese.
  </p>

  <div class="add-subtract-items">
    <button class="subtract-item">
      -
    </button>
    <span class="count">0</span>
    <button class="add-item">+</button>
  </div>

  <p class="total-price">0.00</p>
</div>

<div class="food-item">
  <h3 class="item-title">Milkshake</h3>
  <span class="cost">4.35</span>
  <p class="item-description">
    Tastes like a five-dollar shake, for a little bit less.
  </p>

  <div class="add-subtract-items">
    <button class="subtract-item">
      -
    </button>
    <span class="count">0</span>
    <button class="add-item">+</button>
  </div>

  <p class="total-price">0.00</p>
</div>

JS Fiddle demo.

References:

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement