skip to Main Content

I’m working on a shopping cart functionality for a fictitious online store. The core of this functionality is a Cart class that manages the information and performs calculations. This class doesn’t interact with the HTML document or the DOM, it simply performs calculations.

The Cart class haves methods to manage products. Each product has a SKU, title, price properties and I’m trying to add a quantity property too but it’s not working I think. I have two buttons, addButton and subtractButton, for each product to increase or decrease the quantity.

I want to achieve the following:

  1. When the addButton is clicked, the quantity of the product should increase by 1, and the total price for that SKU should be updated in the total-SKU span.
  2. When the subtractButton is clicked, the quantity of the product should decrease by 1 (if it’s greater than 0), and the total price for that SKU should be updated in the total-SKU span.
  3. The total price of all SKUs should be calculated and displayed in the total div.

Here’s the code in jsFiddle: https://jsfiddle.net/mlpz2/edfjLhb2

Here’s the code here:

class Cart {
    constructor(products, currency) {
        this.products = [];
        this.currency = "";
    }
    initializeQuantity = () => {
        for (let product of this.products) {
          product.quantity = 0; // Initialize quantity to 0 for each product
        }
        console.log(this.products.quantity);
      };
    updateUnits = (sku, units) => {
        // Update the number of units to be purchased for a product
        let product = this.products.find((p) => p.sku === sku);
        if (product) {
            product.quantity = units;
            console.log(this.products.quantity);
        } else {
            this.products.push({ sku: sku, quantity: units });
        }
    };

    getProductInformation = (sku) => {
        // Returns the data of a product along with the selected units
        // For example:
        // {
        // "sku": "0K3QOSOV4V",
        // "quantity": 3
        // }
        return this.products.find((p) => p.sku === sku);
    };

    getAllProducts = () => {
        return this.products;
      };

    getCart = () => {
        // Returns information about the products added to the cart
        // Along with the calculated total of all products
        // For example:
        // {
        // "total": "5820",
        // "currency: "€",
        // "products" : [
        // {
        // "sku": "0K3QOSOV4V"
        // ..
        // }
        // ]}
        let total = this.products.reduce(
            (sum, p) => sum + p.quantity * p.price,
            0
        );
        return { total: total, currency: this.currency, products: this.products };
    };
}

let cart = new Cart();
cart.initializeQuantity();
const getProductsData = async () => {
    let response = await fetch(
        "https://jsonblob.com/api/jsonBlob/1241305513466912768"
    );
    let data = await response.json();
    console.log(data);
    return data; // return the full response
};

const showProducts = (products) => {
    console.log(products);
    
    let productsContainer = document.getElementById("productsContainer");
    for (let product of products) {
        let quantity = product.quantity || 0; // Initialize quantity here

        let productElement = document.createElement("div");
        productElement.innerHTML = `
                        <h2>${product.title}</h2>
                        <p>Ref: ${product.SKU}</p>
                        <p>Price: ${product.price}€/unit</p>
                        <button class="substractButton">-</button>
                        <span id="quantity-${product.SKU}">${quantity}</span>
                        <button class="addButton">+</button>
                        <p>Total: <span id="total-${product.SKU}">0</span>€</p>
                `;
        productElement.className = "product";
        productsContainer.appendChild(productElement);
        let addButtons = productElement.querySelectorAll(".addButton");
        for (let i = 0; i < addButtons.length; i++) {
            addButtons[i].addEventListener('click', () => updateQuantity(product.SKU, 1));
        }
        console.log(addButtons);
        let subtractButtons = productElement.querySelectorAll(".substractButton");
        for (let i = 0; i < subtractButtons.length; i++) {
            subtractButtons[i].addEventListener('click', () => updateQuantity(product.SKU, -1));
            console.log(typeof subtractButtons[i], subtractButtons[i]);
        }
    }
    console.log(productsContainer);
};


const updateTotal = (sku) => {
    let products = cart.getAllProducts(); // Assuming getCart returns an array of products
    let total = 0;
    console.log(products); // Check if the products are being fetched correctly

    for (let product of products) {
        total += product.quantity * product.price;
    }
    document.getElementById('total').textContent = `TOTAL: ${total}`; // Assuming 'total' is the id of the element displaying the total price
};

const updateQuantity = (sku, change) => {
    let product = cart.getProductInformation(sku);
    if (product) {
        product.quantity += change;
        if (product.quantity < 0) {
            // Ensure the quantity doesn't go below 0
            product.quantity = 0;
        }
        document.getElementById(`quantity-${sku}`).textContent = product.quantity;
        document.getElementById(`total-${sku}`).textContent =
            product.quantity * product.price;
        updateTotal();
    }
};

getProductsData().then((data) => {
    if (data && data.products) {
        cart.products = data.products;
        cart.products = data.products.map((product) => {
            return {
                ...product,
                price: parseFloat(product.price),
            };
        }); // assign the currency to the cart object
        showProducts(cart.products);
        updateTotal();
    } else {
        console.error("Failed to fetch products");
    }
});

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <div id="productsContainer">
        <div id="total">
            <!-- The total will be shown here -->
        </div>
    </div> 
<script src="script.js"></script>
</body>

</html>
#productsContainer {
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    margin-top: 20px;
    position: relative;
}

.product {
    width: 200px;
    margin: 10px;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    text-align: center;

}

#total {
    position: absolute;
    font-size: 20px;
    font-weight: bold;
    bottom: -20px;
    left: 10px;
    background-color: #ececec;
}

@media (min-width: 768px) {
    #productsContainer {
        flex-direction: row;
    }
}

The products are fetched from an API. I’m using jsonblob.com(https://jsonblob.com/api/jsonBlob/1241305513466912768) to create a mock API.

{
  "currency": "€",
  "products": [
    {
      "SKU": "0K3QOSOV4V",
      "title": "iFhone 13 Pro",
      "price": "938.99"
    },
    {
      "SKU": "TGD5XORY1L",
      "title": "Cargador",
      "price": "49.99"
    },
    {
      "SKU": "IOKW9BQ9F3",
      "title": "Funda de piel",
      "price": "79.99"
    }
  ]
}

I think I’m not sure how to:

  1. Create new elements in the DOM for the product list.
  2. Listen for events to update the number of units for each product.
  3. Update the total price in the DOM whenever a change is made.

I’m not sure how to create the event listeners for the addButton and subtractButton and how to update the quantity and total price in the DOM.

I’m also open to any suggestions for improving the code. Any help would be appreciated!

Thank you for reading 🙂

2

Answers


  1. Add the event listener to all the buttons seperately and check the events with the id for updating specific elements.

    hope this helps.

    class Cart {
        constructor(products, currency) {
            this.products = [];
            this.currency = "";
        }
        initializeQuantity = () => {
            for (let product of this.products) {
              product.quantity = 0; // Initialize quantity to 0 for each product
            }
            console.log(this.products.quantity);
          };
        updateUnits = (sku, units) => {
            // Update the number of units to be purchased for a product
            let product = this.products.find((p) => p.sku === sku);
            if (product) {
                product.quantity = units;
                console.log(this.products.quantity);
            } else {
                this.products.push({ sku: sku, quantity: units });
            }
        };
    
        getProductInformation = (sku) => {
            // Returns the data of a product along with the selected units
            // For example:
            // {
            // "sku": "0K3QOSOV4V",
            // "quantity": 3
            // }
            return this.products.find((p) => p.SKU === sku);
        };
    
        getAllProducts = () => {
            return this.products;
          };
    
        getCart = () => {
            // Returns information about the products added to the cart
            // Along with the calculated total of all products
            // For example:
            // {
            // "total": "5820",
            // "currency: "€",
            // "products" : [
            // {
            // "sku": "0K3QOSOV4V"
            // ..
            // }
            // ]}
            let total = this.products.reduce(
                (sum, p) => sum + p.quantity * p.price,
                0
            );
            return { total: total, currency: this.currency, products: this.products };
        };
    }
    
    let cart = new Cart();
    cart.initializeQuantity();
    const getProductsData = async () => {
        let response = await fetch(
            "https://jsonblob.com/api/jsonBlob/1241305513466912768"
        );
        let data = await response.json();
        console.log(data);
        return data; // return the full response
    };
    
    const showProducts = (products) => {
        console.log(products);
        
        let productsContainer = document.getElementById("productsContainer");
        for (let product of products) {
            let quantity = product.quantity || 0; // Initialize quantity here
    
            let productElement = document.createElement("div");
            productElement.innerHTML = `
                            <h2>${product.title}</h2>
                            <p>Ref: ${product.SKU}</p>
                            <p>Price: ${product.price}€/unit</p>
                            <button class="substractButton" id=${product.SKU}>-</button>
                            <span id="quantity-${product.SKU}">${quantity}</span>
                            <button class="addButton" id=${product.SKU}>+</button>
                            <p>Total: <span id="total-${product.SKU}">0</span>€</p>
                    `;
            productElement.className = "product";
            productsContainer.appendChild(productElement);
            let addButtons = productElement.querySelectorAll(".addButton");
               addButtons.forEach(button => {
                button.addEventListener('click', event => {
                    updateQuantity(event.target.id, 1);
                });
              });
            
            
            console.log(addButtons);
            let subtractButtons = productElement.querySelectorAll(".substractButton");
            
            subtractButtons.forEach(button => {
                button.addEventListener('click', event => {
                    updateQuantity(event.target.id, -1);
                    console.log(event.target);
                });
              });
        }
        console.log(productsContainer);
    };
    
    
    const updateTotal = () => {
        let products = cart.getAllProducts(); // Assuming getCart returns an array of products
        let total = 0;
        console.log(products); // Check if the products are being fetched correctly
        for (let product of products) {
            if(product.quantity){
                total += product.quantity * product.price;
            }   
        }
    
        document.getElementById('total').textContent = `TOTAL: ${total}`; // Assuming 'total' is the id of the element displaying the total price
    };
    
    const updateQuantity = (sku, change) => {
        
        let product = cart.getProductInformation(sku);
        if (product) {
            if(!product.quantity){
                product.quantity = 0;
            }
            product.quantity += change;
            if (product.quantity < 0) {
                // Ensure the quantity doesn't go below 0
                product.quantity = 0;
            }
            document.getElementById(`quantity-${sku}`).textContent = product.quantity ;
            document.getElementById(`total-${sku}`).textContent =
                product.quantity * product.price;
            updateTotal(product.quantity * product.price);
        }
    };
    
    getProductsData().then((data) => {
        if (data && data.products) {
            cart.products = data.products;
            cart.products = data.products.map((product) => {
                return {
                    ...product,
                    price: parseFloat(product.price),
                };
            }); // assign the currency to the cart object
            showProducts(cart.products);
            updateTotal();
        } else {
            console.error("Failed to fetch products");
        }
    });
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div id="productsContainer">
            <div id="total">
                <!-- The total will be shown here -->
            </div>
        </div> 
        <script src="./product.js"></script>
    </body>
    </html>
    Login or Signup to reply.
  2. After being through your code throughly, I’ve found this two major fixes

    1. p.sku you are accessing the invalid property of product through the class, so just change it to p.SKU
    2. p.quantity is not in initial product object that why it shows NAN it can be fixed by adding that while initialising so you won’t need any if condition to check that.
    cart.products = data.products.map(product => {
        return {
            ...product,
            price: parseFloat(product.price),
            quantity: 0 // THIS WILL FIX MAJOR PROBLESM
        };
    }); 
    

    Also some recommendations, you won’t need querySelectorAll since there is only one .addButton, the forEach will alway run once. Instead try getElementsByClassName or querySelector

    /* 
    let subtractButtons = productElement.querySelectorAll(".substractButton");
    
    for (let i = 0; i < addButtons.length; i++) {
        addButtons[i].onclick(() => console.warn(1));
    } 
    */
    
    let addButtons = productElement.getElementsByClassName("addButton")[0];
            
    addButtons.onclick = () => updateQuantity(product.SKU, 1);
    

    JSFIDDLE

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search