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

Caleb Taylor
Caleb Taylor
6,108 Points

Don't understand why I get undefined when using var

In the lesson Getting Started With ES2015 in the loops part, I understand most of the video about the difference between let and var, so I changed the code a bit where I kept var in the loop and instead of alerting the strings with the i value, it would show the innerHTML instead. The 1st code below works.

<html>
  <head>
    <title>buttons - let and Const</title>
  </head>
  <body>
    <h1>Buttons</h1>
    <button>Button 0</button>
    <button>Button 1</button>
    <button>Button 2</button>
    <button>Button 3</button>
    <button>Button 4</button>
    <button>Button 5</button>
    <button>Button 6</button>
    <button>Button 7</button>
    <button>Button 8</button>
    <button>Button 9</button>
    <script>
      const buttons = document.getElementsByTagName("button");

      for(var i = 0; i < buttons.length; i++) {
          const button = buttons[i]
          button.addEventListener("click", function() {
            // Replaced "Button " + i, with button.innerHTML
              alert(button.innerHTML + " Pressed");
          });
      }
    </script>
  </body>
</html>

However I wanted to get rid of the declaring of the button inside the loop and replace them all with buttons[i] as seen in the second code below. I thought it would still work because the extra declaring constant makes it more readable but I wanted to test without it. But I get an error: Uncaught TypeError: Cannot read property 'innerHTML' of undefined at HTMLButtonElement.<anonymous>. Replacing the var in the loop with let solves the problem. But I don't understand why in this case the var would mess this up.

<html>
  <head>
    <title>buttons - let and Const</title>
  </head>
  <body>
    <h1>Buttons</h1>
    <button>Button 0</button>
    <button>Button 1</button>
    <button>Button 2</button>
    <button>Button 3</button>
    <button>Button 4</button>
    <button>Button 5</button>
    <button>Button 6</button>
    <button>Button 7</button>
    <button>Button 8</button>
    <button>Button 9</button>
    <script>
      const buttons = document.getElementsByTagName("button");

      for(var i = 0; i < buttons.length; i++) {
          // Took out const button, replaced all button with buttons[i]
          buttons[i].addEventListener("click", function() {

              alert(buttons[i].innerHTML + " Pressed");
          });
      }
    </script>
  </body>
</html>

1 Answer

Neil McPartlin
Neil McPartlin
14,662 Points

Hi Caleb. Let's just look at your lower code. Just add an extra line containing a console.log like so...

      for(var i = 0; i < buttons.length; i++) {
          // Took out const button, replaced all button with buttons[i]
          buttons[i].addEventListener("click", function() {
              console.log('What does i = ?', i);
              alert(buttons[i].innerHTML + " Pressed");
          });
      }

Now look in your Chrome devtools and press any button. Yep, it says 10. So although alert(buttons[9].innerHTML + " Pressed"); works if you paste this into your console, alert(buttons[10].innerHTML + " Pressed"); fails with the error you are seeing as this entry does not exist.

So no matter which button you press, the for loop will always run 10 times i.e. starting with 0 and stopping at 9 BUT momentarily i will equal 10 before the i < buttons.length condition kicks in and stops the addEventListener function from running.

For fun, just edit the code a little more and change this line to...

(var i = 0; i < buttons.length - 1; i++)

All we are doing is preventing i from reaching 10. The error message has stopped but no matter which button gets pressed, the alert will always say Button 9 Pressed. (The actual Button 9 no longer works because the loop as been stopped early).

So the issue is not so much with var or const or let, the issue is that we must use one of them to ensure that for each loop, we lock down which value for i is being detected as the actual button being pressed.