2

I want to add innerHTML to my class without changing the text when I hover over it.

For example I have a list of <p> elements with class="food", and when I hover it I want it to go from saying "Chicken" to "Chicken [EAT]".

This is what I have so far, but when I hover the paragraphs, it changes to the last food item, as expected.

var food = document.getElementsByClassName("food");
var len = food.length;

for (i = 0; i < len; i++) {
  addEvent(food[i]);
  var foodName = food[i].innerHTML;

  function addEvent(food) {
    food.addEventListener("mouseover", function(event) {
      event.target.innerHTML = foodName + " [EAT]";
    });
  }
}
<p class="food">Snackbar</p>
<p class="food">Apple</p>
<p class="food">Pear</p>
<p class="food">Chicken</p>
<p class="food">Peas</p>
j08691
  • 204,283
  • 31
  • 260
  • 272
Minnie
  • 23
  • 3
  • 1
    You **really** should be doing this using CSS, **not Javascript**, unless you want a permanent change once hovered. – connexo Oct 25 '19 at 22:00

6 Answers6

2

First move var foodName = food.textContent; inside addEvent

Also don't declare functions inside the loop (not a cause of your problem but not good practice)

Also use textContent instead of innerHTML

var food = document.getElementsByClassName("food");
var len = food.length;

function addEvent(food){
    var foodName = food.textContent;
    food.addEventListener("mouseover", function(event){
        event.target.textContent = foodName + " [EAT]";
    });
}

for( i = 0; i < len ; i++){
    addEvent(food[i]);   
}
<p class="food">Snackbar</p>
<p class="food">Apple</p>
<p class="food">Pear</p>
<p class="food">Chicken</p>
<p class="food">Peas</p>
Willman.Codes
  • 1,352
  • 1
  • 5
  • 13
2

If all you're trying to do is show "[EAT]" you can do that very easily with CSS, no JS required:

.food:hover::after {
  content: " [EAT]";
}
<p class="food">Snackbar</p>
<p class="food">Apple</p>
<p class="food">Pear</p>
<p class="food">Chicken</p>
<p class="food">Peas</p>
GammaGames
  • 1,617
  • 1
  • 17
  • 32
  • 1
    This should be the accepted answer. People should stop using the wrong technology for the tasks. – connexo Oct 25 '19 at 21:57
1

var food = document.getElementsByClassName("food");
var len = food.length;

for (i = 0; i < len; i++) {
  addEvent(food[i]);
  var foodName = food[i].innerHTML;

  function addEvent(food) {
    food.addEventListener("mouseover", function(event) {
      event.target.innerHTML = event.target.innerHTML+ " [EAT]";
    });
    food.addEventListener("mouseleave", function(event){
        event.target.innerHTML = event.target.innerHTML.replace(" [EAT]",'');
    });
  }
}
<p class="food">Snackbar</p>
<p class="food">Apple</p>
<p class="food">Pear</p>
<p class="food">Chicken</p>
<p class="food">Peas</p>
Mert Çelik
  • 334
  • 1
  • 10
1

This is ultimately because of a scoping issue. You are calling a method inside of your for loop, but you are referencing a variable that is scoped to the enclosing function or in this case globally since there is no function (see comment), so the final value assigned is the last pass of the for loop. Changing the declaration of var foodName to let foodName would also resolve the problem by scoping the variable to your loop. The other option would be to pass in what you need to your addEvent and move the function out of your for loop.

var food = document.getElementsByClassName("food");
var len = food.length;

for (i = 0; i < len; i++) {
  var foodName = food[i].innerHTML;
  addEvent(food[i], foodName);
}

function addEvent(food, name) {
  food.addEventListener("mouseover", function(event) {
    event.target.innerHTML = name + " [EAT]";
  });
}
<p class="food">Snackbar</p>
<p class="food">Apple</p>
<p class="food">Pear</p>
<p class="food">Chicken</p>
<p class="food">Peas</p>
TJ Rockefeller
  • 3,178
  • 17
  • 43
  • *but you are referencing a variable that is scoped inside of the for loop* The variable is declared with `var` so it's not scoped to the loop, it's scoped to the enclosing function or if no function, Global. The problem is that there is a closure around `i`. Changing `var` to `let` does scope the variable to the loop and will solve the problem. – Scott Marcus Oct 25 '19 at 21:21
  • @ScottMarcus Thanks for that clarification. – TJ Rockefeller Oct 25 '19 at 21:38
1

You're so close! The problem is that you are running into the classic variable in a loop closure problem. Your one i variable is being shared through all iterations of your loop and because your mouseover callback function references i, the variable isn't garbage collected and remains in memory for as long as the DOM elements that have the mouseoever callbacks are in memory. And because the callbacks don't run until AFTER the loop is finished, all of them refer to the same i value the loop had when it finished, and this is why you always get the last food item.

Simply change the variable declaration from var to let and the problem is solved. let creates "block level" scope - - each iteration of the loop creates its own scope for the loop variable, so instead of all the iterations sharing the same i value, each iteration gets its own i value.

var food = document.getElementsByClassName("food");
var len = food.length;

for (i = 0; i < len; i++) {
  addEvent(food[i]);
  let foodName = food[i].innerHTML;

  function addEvent(food) {
    food.addEventListener("mouseover", function(event) {
      event.target.innerHTML = foodName + " [EAT]";
    });
  }
}
<p class="food">Snackbar</p>
<p class="food">Apple</p>
<p class="food">Pear</p>
<p class="food">Chicken</p>
<p class="food">Peas</p>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
0

Try (css only)

.food:hover::after {
  content: ' [EAT]'
}
<p class="food">Snackbar</p>
<p class="food">Apple</p>
<p class="food">Pear</p>
<p class="food">Chicken</p>
<p class="food">Peas</p>
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345