Set .data() attribute equal to array index

I’m building a table dynamically with jQuery and I want to remove the item from the array when the remove button is clicked on that row. My current approach is to set the data id equal to the current array index and then using that value with the splice() method.

At the moment the remove button removes one or many items, and the last item won’t be removed because it says that the ‘index’ is undefined.

I’m very new at JavaScripr and jQuery so any insight and help will be appreciated.

Here is my code.

$('.shoppingCart').ready(function loadCart() {
// Check to see if cart has items loaded
if (sessionStorage.getItem('cartItemsLoaded' === null)) {
    sessionStorage.setItem("shopCart", JSON.stringify(cart));
    sessionStorage.setItem("cartItemsLoaded", true);
} else {
    // Retrieve array from sessionStorage
    cart = JSON.parse(sessionStorage.getItem("shopCart"));
    cart = cart !=null ? cart : [];

    // Declare iterator
    let cartItems;
    // Loop over array objects with forEach and create <tr> for each object.
    $.each(cart, function (index, value) {
        // Create <tr> element to hold items
        cartItems = $("<tr>" +
            "<td>" + value.prod + "</td>" +
            "<td>R" + value.price + "</td>" +
            "<td>" + "<input type='number' class='form-control border border-dark rounded' id='qntCount' data-name=" + value.prod + " value='" + value.count + "'>" + "<label class='form-label' for='qntCount'>Qnt</label>" + "</td>" +
            "<td>" + "<div class='form-group'>" + "<input type='text' inputmode='numeric' class='form-control-plaintext border border-dark rounded' id='total' data-name=" + value.prod + " value='" + value.count * parseFloat(value.price) + "' disabled>" + "<label class='input-label' for='total'>Total</label>" + "</div>" + "</td>" +
            "<td>" + "<button class='btn btn-outline-danger removeBtn' data-id=' " + index + " '>" + "<i class='bi bi-cart-dash'></i>" + "</button>" + "</td>" +
            "</tr>");

// Used to check value of data-id.
            let i = $('.removeBtn').data('id');
            console.log(i);

        // Add eventListener to removeBtn
        $(".removeBtn").on('click', function removeProd() {
            let i = parseInt($(this).data('id'));
            // Remove object from cart at selected index
            cart.splice(i, 1);
            // Store current cart array
            sessionStorage.setItem("shopCart", JSON.stringify(cart));
            // Reload page
            location.reload();
        });

        $("#shpCart").append(cartItems);
        console.log(i);
        $("#shpCart").show();
    })
}
$(".cartTotal").html(cartTotal());

});

Answer

You bind the event to all the buttons on every iteration. You bind the event before you add the button to the DOM so it does not find the last one.

Reading your code out loud

  • Loop Start
  • Build TR (1)
  • Select all removeBtn on the page and add event (finds nothing)
  • Add row (1)
  • Show cart
  • Next Iteration
  • Build TR (2)
  • Select all removeBtn on the page and add event (finds 1)
  • Add row (2)
  • Show cart
  • Next Iteration
  • Build TR (3)
  • Select all removeBtn on the page and add event (finds 1, 2)
  • Add row (3)
  • Show cart

So with 3 rows.

  • First button has 2 click events attached
  • Second button has 1 click event attached
  • Third button has 0 click events attached

You need to bind the event AFTER you add all of the rows

$.each(cart, function (index, value) {
  // add the rows
});
$(".removeBtn").on('click', function {});