Home > Software engineering >  Cannot read properties of undefined
Cannot read properties of undefined

Time:10-07

I am trying to make a menu for practice and am having issues with making something simply appear and disappear on click of a class called "option". For some reason, when you click on the image, text, or icon nested inside the option div, I get an error saying that it cannot read properties of undefined. I know it is not undefined, or so I think because I can log the outerHTML of the object I'm clicking, but can't add a class to it. I'm new to web development so any help, even if not the full answer that will lead me in the correct direction is very helpful and appreciated :)

let counter = 0;
const icon = document.querySelector('.dropdown-arrow');
const allIcon = document.querySelectorAll('.dropdown-arrow');
const options = document.querySelectorAll(".option");
const foodItem = document.querySelectorAll('.food-item')
const optionContainer = document.querySelectorAll('.option-container');

options.forEach(function(option) {
  option.addEventListener("click", function(event) {
    let element = event.target;
    let currentArrow = element.getElementsByTagName('ion-icon')[0];
    if (counter == 0) {
        // ARROW CLASS FUNCTIONALITY ON CLICK
        console.log(currentArrow.outerHTML);
        currentArrow.classList.add('rotate-on');
        currentArrow.classList.remove('rotate-off');
        counter  ;

        //MENU DESC. FUNCTIONALITY
        foodItem.forEach(function(food) {
            food.classList.remove('hidden');
        })
    } else {
        // ARROW CLASS FUNCTIONALITY ON CLICK
        currentArrow.classList.add('rotate-off');
        currentArrow.classList.remove('rotate-on');
        counter--;
        
        //MENU DESC. FUNCTIONALITY
        foodItem.forEach(function(food) {
            food.classList.add('hidden');
        })
    }
  });
});
.rotate-on {
  transform: rotate(-180deg);
  transition: all .3s;
}

.rotate-off {
  transform: rotate(0deg);
  transition: all .3s;
}

.hidden {
  display: none;
}
<section class = "section section-menu">
        <div class = "option">
            <div class = "option-container">
                <img src = "img/Breakfast-Figma.png" alt="Breakfeast img">
                <h3 class = "option-text">&nbsp;&nbsp;Breakfeast</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
        <div class="food-item hidden">
            <div class = "food-img-container">
                <img src = "img/_0000s_0000_Stack620_0000_CFA_1605_60_Biscuit_Chicken_PROD_2155_1240px.png">
            </div>
            <div class="h1-container">
                <h1 class = "item-name">Chick-fil-A<sup style = "font-size: 60%;">&reg;</sup> Chicken Biscuit</h1>
            </div>
            <div class = "item-info">
                <div class = "cal-container">
                    <span class = "food-price">Sandwhich Only: $2.45 <em>or</em> Meal: $4.89<span style = "color:inherit;"></span></span>
                </div>
                <div class = "cal-container">
                    <span class = "food-cal">460 Cal per biscuit</span>
                </div>
                <div class="food-desc">
                    <span class = "desc-option"></span>
                </div>
            </div>
        </div>
        <div class="food-item hidden">
            <div class = "food-img-container">
                <img src = "img/_0000s_0014_[Feed]_0000s_0024_Breakfast_Chicken-Mini-4ct.png">
            </div>
            <div class="h1-container">
                <h1 class = "item-name">Chick-fil-A Chick-n-Minis<sup style = "font-size: 70%;">&trade;</sup></h1>
            </div>
            <div class = "item-info">
                <div class = "cal-container">
                    <span class = "food-price">Sandwhich Only: $3.29 <em>or</em> Meal: $5.75<span style = "color:inherit;"></span></span>
                </div>
                <div class = "cal-container">
                    <span class = "food-cal">360 Cal per 4 Chick-n-Minis
                    </span>
                </div>
                <div class="food-desc">
                    <span class = "desc-option"></span>
                </div>
            </div>
        </div>

        <div class = "option">
            <div class = "option-container">
                <img src = "img/CFA-Spicy-Chicken-Entree-Figma.png" alt="Spicy Chicken img">
                <h3 class = "option-text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Entreés</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
        <div class = "option">
            <div class = "option-container">
                <img src = "img/Salad-Figma.png" alt="Salad img">
                <h3 class = "option-text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Salads</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
        <div class = "option">
            <div class = "option-container">
                <img src = "img/Large-Fries-Sides-Figma.png" alt="Fries img">
                <h3 class = "option-text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sides</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
        <div class = "option">
            <div class = "option-container">
                <img src = "img/Kids-Meal-Figma.png" alt="Kids Meal img">
                <h3 class = "option-text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Kid's Meals</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
        <div class = "option">
            <div class = "option-container">
                <img src = "img/Milkshake-Treat-Figma.png" alt="Milkshake img">
                <h3 class = "option-text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Treats</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
        <div class = "option">
            <div class = "option-container">
                <img src = "img/Beverage-Figma.png" alt="Beverage img">
                <h3 class = "option-text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Drinks</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
        <div class = "option">
            <div class = "option-container">
                <img src = "img/CFA-Dipping-Sauce-Figma.png" alt="Dipping Sauce img">
                <h3 class = "option-text">&nbsp;&nbsp;&nbsp;&nbsp;Dipping Sauces & Dressings</h3>
            </div>
            <ion-icon name="chevron-up-circle-outline" class = "dropdown-arrow"></ion-icon>
        </div>
    </section>

CodePudding user response:

This happens because your element is not what you expect, it is an inner child of option, so a simple solution is to disable click event on inner elements so the click is triggered on the option element like this:

h3,
.option-container {
  pointer-events: none;
}

or you also could test if the click is inside your option element in js like this:

let element = event.target;
if(element.closest('.option')) element = element.closest('.option');

after you set your element you can select closest option.

I'd go with the css solution.

You can play with them on this Testing pen

CodePudding user response:

First of all, you should wrap your javascript code which access the DOM in:

window.addEventListener('load', function() {
//...
});

to ensure that the DOM has already loaded. Otherwise options could be empty.

Next, the line let element = event.target; is problematic, as event.target isn't necessarily an option. If you print it out you could see it may be option-text or img. It depends on which element you click on.

Replace let element = event.target; with let element = option; and you'll be fine, as option is captured by the closure.

CodePudding user response:

I get an error saying that it cannot read properties of undefined

It should tell you which line is throwing this, number or call, which you really need. Error messages are very important to read.

I know it is not undefined, or so I think

Code never lies. Whatever 'it' is, is undefined. 'It' might not be what you seem to think (currentArrow).

Instead of trying to guess the value of something based on its behavior, just ask for the value. That is to say use console.log(currentArrow) instead. If currentArrow is not undefined, then something else is causing your error.

CodePudding user response:

When you console.log, let currentArrow = element.getElementsByTagName('ion-icon')[0]; outside of the if statement. You will observe that it is UNDEFINED. Please remove ".getElementsByTagName('ion-icon')[0]" and you will get a better result as 'element' does not have that property.

  • Related