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 JavaScript and the DOM (Retiring) Responding to User Interaction Event Bubbling and Delegation

Can Tural
Can Tural
13,053 Points

Loop Error

Hi, I used 'var' instead of 'let' in this loop and It gave an error. Is there anyone who understands why? When I change the var keyword to let It works.

for (var i = 0; i < listItems.length; i++) {
    listItems[i].addEventListener('mouseover', () => {
        listItems[i].textContent = listItems[i].textContent.toUpperCase();
    });
}
ERROR: 
Uncaught TypeError: Cannot read property 'textContent' of undefined
    at HTMLLIElement.listItems.(anonymous function).addEventListener

Can you post the rest of your code? The part where you declare listItems and your html would be most helpful.

3 Answers

Hi Can,

This is a tricky one, isn't it? As you've astutely noted, the issue you're experiencing here is due to the discrepancy between the keywords var and let in JavaScript.

What's happening is that, when you use var in your for-loop, the value of i is set globally. This means that by the time your event-listeners finally get called — on 'mouseover' and 'mouseleave' — they will refer to the value of i that exists after the for-loop has finished all of its iterations. In your case, for example — since you only have one listItem — your event-listeners will try to access the textContent of the listItem at index 1... but, of course, there is no listItem at that index, hence your error.

Conversely, a variable declared using let is not set globally. Instead, using let allows you to let a variable exist exclusively within a specific lexical scope. This means that your event-listeners will capture the value of i as they get assigned, as opposed to just capturing the reference to a global i like we were doing with var.

If you want to see some code examples of the difference between these two keywords, check out the MDN Documentation on let.

Can Tural
Can Tural
13,053 Points

Hi,

This is the HTML Code:

<!DOCTYPE html>
<html>
<head>
  <title>Javascript</title>
  <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <div>
    <h1>Best Tv Shows</h1>
  <ol class="my-list">
    <li>Mad Man</li>
  </ol><br>
  <p class="warning">Please add a TV show!</p>
  <input type="text" class="list-input">
  <button class="add-item">Add New Item</button>
  <button class="remove-item">Remove Last Item</button><br>
<script type="text/javascript" src="script.js"></script>
</body>
</html>

JavaScript CODE:

//Selected Elements
var ol = document.querySelector('.my-list');
var listInput = document.querySelector('.list-input');
var addItem = document.querySelector('.add-item');
var removeItem = document.querySelector('.remove-item');
var p = document.querySelector('.warning');
var listItems = document.getElementsByTagName('li');

//UpperCase and LowerCase
for (let i = 0; i < listItems.length; i++) {
    listItems[i].addEventListener('mouseover', () => {
        listItems[i].textContent = listItems[i].textContent.toUpperCase();
    });
    listItems[i].addEventListener('mouseleave', () => {
        listItems[i].textContent = listItems[i].textContent.toLowerCase();
    });
}


//Add and Remove
addItem.addEventListener('click', () =>{
    //If the user doesn't type sth
    if(listInput.value == ''){
        p.style.display = 'block';
    }else{//When the user types sth
        p.style.display = 'none';
        var li = document.createElement('li');
        li.textContent = listInput.value;
        ol.appendChild(li);
        //This deletes the last input
        if(listInput.value != ''){
            listInput.value = '';
        }
    }
});

removeItem.addEventListener('click', ()=>{
    lastItem = document.querySelector('li:last-child');
    ol.removeChild(lastItem);
});
Can Tural
Can Tural
13,053 Points

Yeah, that was very tricky. Thanks a lot for your detailed explanation. INow it makes perfect sense.