简体   繁体   中英

Multiple event listeners only update the last field that was setup

I am trying to setup an order form where the user can enter a quantity into an input field and the total for that particular product will update. There will be dozens of products in the end.

At the moment, I have the product data stored into a javascript object that is turned into an array to work with. Using this array, I have the products and all fields set up. The price is setup based on a products size. Certain products will have multiple sizes and some only one size.

The problem I am having is that when I loop over the products to setup their event listeners, it only seems to work on the last element of the array. Here is just the sections I think are the culprits:

//Set the listener for each products input box
function SetListeners(_prodList) {
    for(var p in _prodList)
    {
        //We need a listener for each size
        for(var s in _prodList[p].sizes)
        {
            //Get the html of the element in question and add a listener with the product details
            var inputID = 'input-' + _prodList[p].sizes[s].sku;
            document.getElementById(inputID).addEventListener('input', function() {
                UpdatePrice(inputID, _prodList[p].sizes[s].price, 'total-' + _prodList[p].id);
                UpdateTotal();
            });
        }
    }
}

//Updates our total for each product
function UpdatePrice(_id, _price, _total)
{
    var prodQty = document.getElementById(_id).value;
    var prodTotal = (prodQty * _price).toFixed(2);
    document.getElementById(_total).innerHTML = '$' + prodTotal;
}

How can I get it so that every input box registers to a specific total innerHTML based on the products sku?

Here is a fiddle with the whole code running and with formatting: https://jsfiddle.net/BlueVolcano/cx70veh3/1/

Here is the full javascript only:

//The total for the order
var orderTotal = 0;

//The data object containing all the products
const products = {
    "shirts": 
        [
        {
            "name": "Product 1",
            "id": "Prod-1",
            "img": "https://i.imgur.com/Ycfi8RS.png",
            "details": "These are the details for the product.",
            "sizes": [
                {"size": "Large", "sku": "Prod-1-1", "price": 10.99}
            ]
        },
        {
            "name": "Product 2",
            "id": "Prod-2",
            "img": "https://i.imgur.com/Ycfi8RS.png",
            "details": "These are the details for the product.",
            "sizes": [
                {"size": "Large", "sku": "Prod-2-1", "price": 20.99}
            ]
        },
        {
            "name": "Product 3",
            "id": "Prod-3",
            "img": "https://i.imgur.com/Ycfi8RS.png",
            "details": "These are the details for the product.",
            "sizes": [
                {"size": "Large", "sku": "Prod-3-1", "price": 30.99},
                {"size": "Medium", "sku": "Prod-3-2", "price": 26.99},
                {"size": "Small", "sku": "Prod-3-3", "price": 22.99}
            ]
        }
    ]
};

//Sort all products from data object into array and flatten it
var thisProductList = [];
for(var v in products)
    thisProductList.push(products[v]);
thisProductList = thisProductList.flat();

//Category specific lists from the all products
SetProducts(thisProductList, 'shirts');
    
//Creates our products and adds them as new html elements
function SetProducts(_products, _category)
{
    //Create the section header
    var sectionDiv = document.createElement('section');
    sectionDiv.setAttribute("class", _category);
    sectionDiv.innerHTML = '<h2 class="section-header">' + _category.toUpperCase() + '</h2>';
    document.getElementById('container').append(sectionDiv);

    //Loop through the list and create the products
    for(var p in _products)
    {
        var rowDiv = document.createElement('div');
        rowDiv.setAttribute("class","row");
        
        var productDiv = document.createElement('div');
        productDiv.setAttribute("class","product");

        var productImage = document.createElement('div');
        productImage.setAttribute("class","product-image padding");
        productImage.innerHTML = '<img src="'+ _products[p].img + '" />';
        productDiv.append(productImage);
        
        var productName = document.createElement('div');
        productName.setAttribute("class","product-name padding");
        productName.innerHTML = '<h2 class="product-header">' + _products[p].name + '</h2>';
        productDiv.append(productName);
        
        var productDetails = document.createElement('div');
        productDetails.setAttribute("class","product-details");
        productDetails.innerHTML = _products[p].details;
        productName.append(productDetails);
        
        var productSizes = document.createElement('div');
        productSizes.setAttribute("class","product-sizes padding");
        for(var s in _products[p].sizes)
        {
            var temp = document.createElement('div');
            var size = _products[p].sizes[s];
            temp.setAttribute("id","size-"+size.sku); 
            _products[p].sizes.length > 1 ? temp.setAttribute("class","flex underline") : temp.setAttribute("class","flex"); 
            var price= document.createElement('div');
            price.innerHTML = (size.size == '' ? '' : size.size + ': ') + '<strong>$' + size.price.toFixed(2) + '</strong><br/><span class="sku"><i>' + size.sku + '</i></span>';
            price.setAttribute("class","half"); 
            temp.append(price);
            var input = document.createElement("input");
            input.type = "number";
            input.setAttribute("id", "input-" + size.sku);
            input.setAttribute("class", "quantity-input half");
            temp.append(input);
            productSizes.append(temp);
        }
        productDiv.append(productSizes);
        
        var productTotal = document.createElement('div');
        productTotal.setAttribute("id","total-" + _products[p].id);
        productTotal.setAttribute("class","product-total padding");
        productTotal.innerHTML = '$0.00';
        productDiv.append(productTotal);
        
        sectionDiv.append(rowDiv);
        rowDiv.append(productDiv);
    }
    
    SetListeners(_products);
}

//Set the listener for each products input box
function SetListeners(_prodList) {
    for(var p in _prodList)
    {
        //We need a listener for each size
        for(var s in _prodList[p].sizes)
        {
            //Get the html of the element in question and add a listener with the product details
            var inputID = 'input-' + _prodList[p].sizes[s].sku;
            document.getElementById(inputID).addEventListener('input', function() {
                UpdatePrice(inputID, _prodList[p].sizes[s].price, 'total-' + _prodList[p].id);
                UpdateTotal();
            });
        }
    }
}

//Updates our total for each product
function UpdatePrice(_id, _price, _total)
{
    var prodQty = document.getElementById(_id).value;
    var prodTotal = (prodQty * _price).toFixed(2);
    document.getElementById(_total).innerHTML = '$' + prodTotal;
}

//Updates the order total field
function UpdateTotal()
{
    orderTotal = 0;
    var totals = document.getElementsByClassName('product-total');
    for(var i = 0; i < totals.length; i++)
    {
        var temp = totals[i].innerHTML;
        temp = temp.replace('$','');
        var t = parseFloat(temp);
        orderTotal += t;
    }
    document.getElementById('top-order-total-val').innerHTML = '$' + orderTotal.toFixed(2);
}

Your issue is caused by using outside variables ( inputID , p , s ) inside your input handler function. The values of these 3 variables are the values of last product in your loop.

To solve this issue, when generation HTML elements for the products, you could add some more attributes, for storing pricing and its corresponding total's id.

var input = document.createElement("input");
input.type = "number";
input.setAttribute("id", "input-" + size.sku);
input.setAttribute("class", "quantity-input half");
// set additional attribute for calculation
input.setAttribute("price", size.price);
input.setAttribute("total", "total-" + _products[p].id);
input.setAttribute("last-value", "0");

Then in your input handler function, reference these additional attributes for pricing and corresponding total element.

//Get the html of the element in question and add a listener with the product details
var inputID = 'input-' + _prodList[p].sizes[s].sku;
document.getElementById(inputID).addEventListener('input', function() {
    var input = event.target;
    UpdatePrice(input.id, input.getAttribute("price"), input.getAttribute("total"));
    UpdateTotal();
});

In your UpdatePrice function, update formula for total calculation

 {New Total} = {Last Total} - {Last Value Of Current Size} + {New Value Of Current Size}

Updated UpdatePrice function:

function UpdatePrice(_id, _price, _total) {
  var inputElement = document.getElementById(_id);
  var totalElement = document.getElementById(_total);
  var prodQty = inputElement.value;
  var lastTotal = parseInt(totalElement.getAttribute("last-total"));
  var lastValue = parseInt(inputElement.getAttribute("last-value"));
  var newValue = prodQty * _price;
  var prodTotal = lastTotal - lastValue + newValue;
  inputElement.setAttribute("last-value", newValue);
  totalElement.setAttribute("last-total", prodTotal);
  totalElement.innerHTML = '$' + prodTotal.toFixed(2);
}

You could reference it here: https://jsfiddle.net/bt9dz63a/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM