Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

I'm trying to create a 'shopping cart' with Javascript, and I can't loop through my array properly

When I click on the buttons from the top down, it displays the total price at the top of the page. However, when I click from the bottom up, it doesn't work. I get 'basket.js:31 Uncaught TypeError: Cannot read property 'price' of undefined at basket.js:31 at HTMLButtonElement.button.addEventListener (basket.js:56)' Why?

All I'm trying to do, is when you click on the button, it adds the value of that item to a total at the top of the page. However, it only works when I do it in order of top to bottom....why?

<!DOCTYPE html>
<html lang="">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>

<body>

<h1 class="totalBasket"></h1>
<script src="basket.js"></script>  
</body>
</html>
let button = document.querySelector('button');
let body = document.querySelector('body');
let totalBasket = document.querySelector('.totalBasket');


function DrinkBluePrint(name, price) {
    this.name = name;
    this.price = price;
}

let latte = new DrinkBluePrint('Latte', 9);
let flatW = new DrinkBluePrint('Flat White', 5);
let cap = new DrinkBluePrint('Cap', 5);
let moc = new DrinkBluePrint('Moc', 47);


let array = [
   latte,
    flatW,
    cap,
    moc
];


let emptyCart = [];


function loopEmptyCart() {
    let total = 0;
    return function ([i]) {
        total += emptyCart[i].price;
        console.log(total);
        totalBasket.textContent = total;

    }
}


let basket = loopEmptyCart();


(function () {
    let p;
    let button;
    for (let i = 0; i < array.length; i++) {
        p = document.createElement('p');
        button = document.createElement('button');
        button.textContent = 'Add';
        let text = document.createTextNode(array[i].name);
        p.appendChild(text);
        body.appendChild(p);
        body.appendChild(button);

        button.addEventListener('click', () => {
            emptyCart.push(array[i]);
            basket([i]);
        });
    };
})();

Thanks in advance

2 Answers

Steven Parker
Steven Parker
229,644 Points

In the "loopEmptyCart" function we find this code:

    total += emptyCart[i].price;

The variable "i" here is the index number of the pressed button, but the "emptyCart" array is constructed as the buttons are pushed, so each button will begin working after the number of presses have been made equivalent to its index number.

So the top button will work immediately, but the bottom button won't work until a total of 3 button presses have already been made. You can demonstrate this by just pushing it 4 times.

I suspect you'll want to employ a different strategy for this function.

Also, I found this most interesting:

function ([i]) {

I assume you'd call this "argument array destructuring". I didn't realize this was possible until I saw it used here! I don't think you actually need it here, since all it seems to do is require you to add braces around the argument when you call "basket". But it was fascinating to discover that it can be done!

I tried switching it up, and I still don't know whats happening

let button = document.querySelector('button');
let body = document.querySelector('body');
let totalBasket = document.querySelector('.totalBasket');


function DrinkBluePrint(name, price) {
    this.name = name;
    this.price = price;
}

let latte = new DrinkBluePrint('Latte', 10);
let flatW = new DrinkBluePrint('Flat White', 10);
let cap = new DrinkBluePrint('Cap', 10);
let moc = new DrinkBluePrint('Moc', 20);


let array = [
   latte,
    flatW,
    cap,
    moc
];



(function () {
    let cart = [];
    let p;
    let button;
    let total = 0;
    for (let i = 0; i < array.length; i++) {
        p = document.createElement('p');
        button = document.createElement('button');
        button.textContent = 'Add';
        let text = document.createTextNode(array[i].name);
        p.appendChild(text);
        body.appendChild(p);
        body.appendChild(button);    

        button.addEventListener('click', () => { 
           cart.push(array[i]);
            total += array[i].price;
            console.log(total);



        });
    };
})();

When I have the event listener display the total from the array, 'array', I can get it to work from bottom to top.

 button.addEventListener('click', () => { 
           cart.push(array[i]);
            total += array[i].price;
            console.log(total);
        });

When I use the 'cart' array, I can only go from top to bottom.

 button.addEventListener('click', () => { 
           cart.push(array[i]);
            total += cart[i].price;
            console.log(total);
        });

I just don't get it

Steven Parker
Steven Parker
229,644 Points

As I was explaining before, this is because variable "i" is being used for the index number of the pressed button, so it's not the right thing to use to index the "emptyCart" array. For example, let's look at what happens in the handler if the button with index 3 ("moc") is pushed first:

                             // i is 3 because that's the "moc" button index
    cart.push(array[i]);     // array[3] is added to "cart"
                             // cart now contains 1 item (cart[0])
    total += cart[i].price;  // but cart[3] is not valid (yet)!
(function () {



    //let button = document.querySelector('button');
    let body = document.querySelector('body');
    let totalBasket = document.querySelector('.totalBasket');


    function DrinkBluePrint(name, price) {
        this.name = name;
        this.price = price;
    }

    let latte = new DrinkBluePrint('Latte', 10);
    let flatW = new DrinkBluePrint('Flat White', 10);
    let cap = new DrinkBluePrint('Cap', 10);
    let moc = new DrinkBluePrint('Moc', 60);


    let array = [
   latte,
    flatW,
    cap,
    moc
];




    let cart = [];
    let p;
    let button;







    for (let i = 0; i < array.length; i++) {
        p = document.createElement('p');
        button = document.createElement('button');
        button.textContent = 'Add';
        let text = document.createTextNode(array[i].name);
        p.appendChild(text);
        body.appendChild(p);
        body.appendChild(button);

        button.addEventListener('click', (e) => {
            cart.push(array[i]);
            displayTotal();
        });

        function displayTotal() {
            let total = 0;
            for (let i = 0; i < cart.length; i++) {
                total += cart[i].price;
            }
            console.log(total);
            totalBasket.textContent = total;
        }
    };


})();

This solution worked for me. I basically push an item to the 'cart' array and then call a function that sets the total variable to 0. I then loop the 'cart' array and display the total in an h1.

Is this a good way to go about it? I'm trying to keep my code clean and efficient.

Thanks for your help

Steven Parker
Steven Parker
229,644 Points

It looks like you're handling the index for "cart" properly now. :+1:

Hey, Steven. I don't quite understand. My thinking is that if you click on the button, the object associated with that button will be added to the array. You could then loop the array and display the total. I just don't get why I can't go from bottom upwards.