Scope of anonymous function as argument

I am working on a very simple application. When the user hovers over any list item ( li

), the text color changes to green, and when there is no mouse, it returns to black.

Why can't we use lis[i]

in the following code snippet inside an anonymous function instead of a keyword this

?

var lis = document.querySelectorAll('li');
var i = 0;
for(; i < lis.length; i++){

    lis[i].addEventListener('mouseover', function(){

    this.style.color = 'green';

    });
    lis[i].addEventListener('mouseout', function(){

    this.style.color ="black";  
    });
};

      

+3


source to share


3 answers


When the callback function is executed, the variable i

will have a value lis.length

due to the loop, causing the value to lis[i]

be undefined

.

You can use a function instead forEach

.



var lis = document.querySelectorAll('li');
lis.forEach(function (li) {
  li.addEventListener('mouseover', function (){
    li.style.color = 'green';
  });
  li.addEventListener('mouseout', function (){
    li.style.color ="black";  
  });
});
      

<ul>
  <li>First LI</li>
  <li>Second LI</li>
</ul>
      

Run codeHide result


+3


source


When the function is called, the loop will be completed. There is only one variable i

, and the function always sees its current value. Therefore, if you use i

inside a function, you will see it with a value lis.length

.

There are ways to get around this. If you can use ES2015 (possibly via a transpiler) you can write:

const lis = document.querySelectorAll('li');
for(let i = 0; i < lis.length; i++){

    lis[i].addEventListener('mouseover', () => lis[i].style.color = 'green');
    lis[i].addEventListener('mouseout', () => lis[i].style.color ="black");
};

      

and now you have a different one i

for each loop.



Or, in older code, you can output the loop body to another function and pass it i

as a parameter. This has the same effect as binding a variable for each event:

var lis = document.querySelectorAll('li');

var _loop = function _loop(i) {

    lis[i].addEventListener('mouseover', function () {
        return lis[i].style.color = 'green';
    });
    lis[i].addEventListener('mouseout', function () {
        return lis[i].style.color = "black";
    });
};

for (var i = 0; i < lis.length; i++) {
    _loop(i);
}

      

(which is the code automatically generated by babel from the ES2015 example I gave above)

+2


source


you can use "e.srcElement" to get the current target like this

let lis = document.querySelectorAll('li');

for (let i = 0; i < lis.length; i++) {
  lis[i].addEventListener('mouseover', function(e){
    e.srcElement.style.color = 'green';
  })
  lis[i].addEventListener('mouseout', function(e){
    e.srcElement.style.color = 'black';
  })
}
      

<ul>
  <li>First LI</li>
  <li>Second LI</li>
</ul>
      

Run codeHide result


-1


source







All Articles