javascriptevent-listenerdynamic-content

Issue with Dynamic Content Rendering in JavaScript - Variables Not Updating Properly


I've this issue while working on a website implementation that involves dynamic content rendering using JavaScript. The issue are variables not updating as expected, leading to unexpected behavior.

Despite my attempts, the quantities of all items are modified simultaneously when only a single item's quantity should be updated upon clicking the corresponding "Increment" button.

I created an array itemsData to store the item data, and I defined functions renderItems and incrementQuantity to handle rendering and updating the quantities. I used the itemName variable to determine which item's quantity should be incremented.

I expected that when I click the "Increment" button for a particular item, only that item's quantity would increase, and the webpage would reflect the updated quantity. However, the issue is that when I clicked the "Increment" button for one item, the quantities for all items got updated instead, which was not the expected behavior.

Here's the JS and html:

const itemsData = [
    { name: "Item A", quantity: 0 },
    { name: "Item B", quantity: 0 },
    { name: "Item C", quantity: 0 }
];

function renderItems() {
  const container = document.getElementById("item-container");
  container.innerHTML = "";

  itemsData.forEach(item => {
    const itemDiv = document.createElement("div");
    itemDiv.innerHTML = `
            <p>${item.name}</p>
            <p>Quantity: ${item.quantity}</p>
            <button onclick="incrementQuantity('${item.name}')">Increment</button>
        `;
    container.appendChild(itemDiv);
  });
}

function incrementQuantity(itemName) {
  const itemIndex = itemsData.findIndex(item => item.name === itemName);
  if (itemIndex !== -1) {
    itemsData[itemIndex].quantity++;
    renderItems();
  }
}

renderItems();
<div id="item-container">
  <!-- Items will be dynamically added here -->
</div>
<script src="axaf_script.js"></script>


Solution

  • Inline event handlers are garbage, so don't use:

    <button onclick="lame()">LAME</button>
    

    Please refer to events and event delegation for proper event handling.

    Details are commented in example

    // Reference container element.
    const menu = document.querySelector("menu");
    // Define object array of data.
    const data = [
      { name: "Item A", quantity: 0 },
      { name: "Item B", quantity: 0 },
      { name: "Item C", quantity: 0 }
    ];
    
    /**
     * Add <li> to a given element (main). The number of <li> and
     * their content is determined by the given object array (array).
     * Each <li> will be the following:
     *   <li>
     *     Item * <output value="0">0</output>
     *     <button class="sub">-</button>
     *     <button class="add">+</button>
     *   </li>
     * @param {Object<DOM>} main - Element that'll contain
     *        the <li>.
     * @param {array} array - Object array that contains the
     *        content of each <li>.
     */
    function renderItems(main, array) {
      array.forEach(item => {
        const listItem = document.createElement("li");
        listItem.innerHTML = `
          ${item.name} Quantity: <output value="${item.quantity}">0</output> <button class="sub">-</button><button class="add">+</button>`;
        main.append(listItem);
      });
    }
    
    // Event handler passes (event) Object by default.
    function itemQuantity(event) {
      // Reference the element the user clicked.
      const clicked = event.target;
      // Reference the element that contains the clicked element.
      const li = clicked.parentElement;
      
      /*
      If clicked element is a <button> AND clicked element ISN'T
      the element registered to the "click" event (main)...
      */
      if (clicked.matches("button") && clicked != event.currentTarget) {
        // Reference the <output> in parent (li).
        let output = li.querySelector("output");
        // Convert the .value of <output> into a real number.
        let qty = Number(output.value);
        
        // If clicked element has class "add"...
        if (clicked.matches(".add")) {
          // ...increase (qty) by 1...
          qty++;
        // ...otherwise...
        } else {
          // ...decrease (qty) by 1...
          qty--;
          // ...if (qty) is less than 0...
          if (qty < 0) {
            // ...reset (qty) to 0.
            qty = 0;
          }
        }
        // Change <output> to (qty).
        output.value = qty;
      }
    }
    
    /*
    Register <menu> to the "click" event. When "click" event is
    fired, call event handler itemQuantity(event).
    */
    menu.onclick = itemQuantity;
    
    renderItems(menu, data);
    <menu></menu>