Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript

Why does my function work in one section of my code, but not another?

I have a function that creates an h3 and the text for the h3 is taken from the array item you click on. My issue is, that when I click a button from the top down, my code works - it displays the name of the drink (array item). However, I can't click from the bottom up. I keep getting an error. But when I place the exact same function in a different part of the code, it works... I don't understand why -- the function is addCartItem();

-- code that works

(function () {

    let body = document.querySelector('body');
    let totalBasket = document.querySelector('.totalBasket');
    let cartCount = document.querySelector('.cartCount');
    let cartItemsDiv = document.querySelector('.cartItems');


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

    let latte = new DrinkBluePrint('Latte', 5.00);
    let flatW = new DrinkBluePrint('Flat White', 3.60);
    let cap = new DrinkBluePrint('Cap', 2.75);
    let moc = new DrinkBluePrint('Moc', 3.15);
    let cortado = new DrinkBluePrint('Cortado', 3.15);


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


    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.toFixed(2);
            cartCount.textContent = `You have ${cart.length} items in your cart`;
            addCartItem(); // function added here - it works 
            console.log(cart[i].name);
            if (total >= 10) {
                let discountedPrice = (total - 3);
                totalBasket.textContent = '£ ' + discountedPrice.toFixed(2);
            }
        }

        function addCartItem() {
            let title = document.createElement('h3');
            let titleText = document.createTextNode(array[i].name);
            title.appendChild(titleText);
            cartItemsDiv.appendChild(title);
        }

    };


})();

-- code that doesn't work

(function () {

    let body = document.querySelector('body');
    let totalBasket = document.querySelector('.totalBasket');
    let cartCount = document.querySelector('.cartCount');
    let cartItemsDiv = document.querySelector('.cartItems');


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

    let latte = new DrinkBluePrint('Latte', 5.00);
    let flatW = new DrinkBluePrint('Flat White', 3.60);
    let cap = new DrinkBluePrint('Cap', 2.75);
    let moc = new DrinkBluePrint('Moc', 3.15);
    let cortado = new DrinkBluePrint('Cortado', 3.15);


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


    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();
            addCartItem(); // function added here - doesn't work 
        });

        function displayTotal() {
            let total = 0;
            for (let i = 0; i < cart.length; i++) {
                total += cart[i].price;
            }
            console.log(total);
            totalBasket.textContent = '£ ' + total.toFixed(2);
            cartCount.textContent = `You have ${cart.length} items in your cart`;
//            addCartItem();
            console.log(cart[i].name);
            if (total >= 10) {
                let discountedPrice = (total - 3);
                totalBasket.textContent = '£ ' + discountedPrice.toFixed(2);
            }
        }

        function addCartItem() {
            let title = document.createElement('h3');
            let titleText = document.createTextNode(array[i].name);
            title.appendChild(titleText);
            cartItemsDiv.appendChild(title);
        }





    };


})();

Thanks

1 Answer

Steven Parker
Steven Parker
220,426 Points

This issue is very similar to the one in your previous question about this same program. In both cases, the function is depending on the value of variable "i", and it represents different things in the two places you are calling the function.

One thing that might help make things less confusing would be to move function defintions outside of the loop.

Thanks, Steven. I get that the index values won't match up, but could you help me understand why putting the function in the displayTotal function works? They both have access to the same "i" variable at the time the button is clicked. I just don't fully understand why putting it in the displayTotal function makes it work.

I haven't made any real significant changes other than:

 let titleText = document.createTextNode(array[i].name + " " + array[i].price.toFixed(2))

...And now it works. I can add items from the bottom up.... What changed??

(function () {

    let body = document.querySelector('body');
    let totalBasket = document.querySelector('.totalBasket');
    let cartCount = document.querySelector('.cartCount');
    let cartItemsDiv = document.querySelector('.cartItems');


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

    let latte = new DrinkBluePrint('Latte', 5.00);
    let flatW = new DrinkBluePrint('Flat White', 3.60);
    let cap = new DrinkBluePrint('Cap', 2.75);
    let moc = new DrinkBluePrint('Moc', 3.15);
    let cortado = new DrinkBluePrint('Cortado', 3.15);


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


    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();
            addCartItem();
        });

        function displayTotal() {
            let total = 0;
            for (let i = 0; i < cart.length; i++) {
                total += cart[i].price;
            }
            console.log(total);
            totalBasket.textContent = '£ ' + total.toFixed(2);
            cartCount.textContent = `You have ${cart.length} items in your cart`;
//            addCartItem();
            if (total >= 10) {
                let discountedPrice = (total - 3);
                totalBasket.textContent = '£ ' + discountedPrice.toFixed(2);
            }
        }

        function addCartItem() {
            let title = document.createElement('h3');
            let titleText = document.createTextNode(array[i].name + " " + array[i].price.toFixed(2));
            title.appendChild(titleText);
            cartItemsDiv.appendChild(title);
        }


    };


})();
Steven Parker
Steven Parker
220,426 Points

The point I was trying to make is that both locations do not have access to the same "i" variable at the time the button is clicked. As each handler is created, it gets a private copy of "i" with the value it had at the moment "addEventListener" was called. But the value it has in "displayTotal" is the one left over after the loop finishes.

A more "best practice" design would be to pass the index, array item, or name to "addCartItem" as an argument instead of having it rely on a variable it does not have control of.