Michael Meacham
Michael Meacham

Reputation: 21

How to fix "Cannot read property 'getAttribute' of undefined?

I'm trying to make a simple calculator and am stuck after trying to using the getAttribute method to retrieve my custom data-* attribute.

I made sure the var used to store the elements does indeed house them

<button type="button" class="btn btn-orange" data-num="*">*</button>
var btns = document.querySelectorAll(".btn");
for(var i = 0; i < btns.length; i++){
    btns[i].addEventListener("click", function(){
        var number = btns[i].getAttribute("data-num");
        screen.value += number;
    });
}

There are 15 buttons in total with class="btn". I have checked in the console and btns does indeed hold 15 elements. I don't understand why getAttribute("data-num") is returning undefined when I click any of the buttons with the stated class.

Upvotes: 2

Views: 8294

Answers (3)

Steve S.
Steve S.

Reputation: 23

I came to this page with exactly the same problem !! After trying the suggestions and getting the same error, I re-checked the data-items I was using. Because I was working on a diary project, mine had dates:

    <pre>
        <li data-date="#01/01/1900">The Past</li>
        <li data-date="#01/04/1994">Apr '94</li>
        <li data-date="#01/10/1996" class="selected">Oct '96</li>
        <li data-date="#01/09/2005">Sep '05</li>
    </pre>

These were designed to align with a second list containing further information:

    <pre>
        <li id="01/01/1900">
        <li id="01/04/1994">
        <li id="01/10/1996" class="selected">
        <li id="01/09/2005">
    </pre>

I knew that I had the correct selectors and eventListener, as I had used similar code previously:

    <pre>
            const events = document.querySelectorAll(".events ol li");

            events.forEach((event) => {
                event.addEventListener("click", () => {
  
                    const eventDate = this.getAttribute("data-date");
                    const eventInfo = document.querySelector(eventDate);

                    console.log(eventDate);
                    console.log(eventInfo);
                });
            });
    </pre>

But the error persisted !!

It was only when I tried changing the data-date and ID items to simply "first", "second", "third", "fourth" etc that the errors disappeared, and my code worked as expected.

In conclusion: remove any punctuation from your IDs and data-items.

Upvotes: 0

Rohit Mittal
Rohit Mittal

Reputation: 2104

You can use this inside function definition instead of btns[i] as below:

var btns = document.querySelectorAll(".btn");
for(var i = 0; i < btns.length; i++){
    btns[i].addEventListener("click", function(){
        var number = this.getAttribute("data-num");
        screen.value += number;
    });
}

Basically you have created click event function for each button and you are using i inside these function. When this function executes, it checks i value which will be the last value of loop, (if you have 6 .btn class then i will have 6) because you are not binding i current value with function. So on each click, you will get last value which will break code.

To avoid this, you can either use above code or can bind variables in function as below:

var btns = document.querySelectorAll(".btn");
for(var i = 0; i < btns.length; i++){
    btns[i].addEventListener("click", function(iBindededValue){
        var number = btns[iBindededValue].getAttribute("data-num");
        screen.value += number;
    }.bind(this, i));
}

Here we can bind i with current value with each function and can use that. You can refer bind function to check its functionality. Hope it helps you to understand its flow, Cheers!!

Upvotes: 2

junvar
junvar

Reputation: 11574

The problem is that i is mutated but you're using it INSIDE the event listener. If you log i when adding the event listeners, you'll get 0, 1, 2, 3, as expected. However, if you log i inside the event listener, i is ALWAYS be 4, and so btns[i] is undefined.

Simply keep a separate reference of i, or better yet of btns[i] as below:

var btns = document.querySelectorAll(".btn");

for(var i = 0; i < btns.length; i++){
    btn = btns[i];
    btn.addEventListener("click", function() {
        console.log(i, btns[i]); // i == 4, btns[i] == undefined !!!!
        var number = btn.getAttribute("data-num");
        console.log(number);
    });
}
<button class='btn' data-num='0'>0</button>
<button class='btn' data-num='1'>1</button>
<button class='btn' data-num='2'>2</button>
<button class='btn' data-num='3'>3</button>

Upvotes: 0

Related Questions