1

I have a problem with the foreach loop. Written code using the foreach loop does not work but if I use the for loops everything works as it should. I look for solution in internet few hours and nothing. I put two examples. First example is use foreach loop and second example is use for loop. Sorry but my english is not the best.

Example - foreach loop (broken):

window.onload = () => {
  test();
};


function test() {
  let testDiv = Array.from(document.getElementsByClassName("test"));
  let currentTest = testDiv[0];
  currentTest.className += " test2";
   testDiv.forEach((current) => {
    current.addEventListener('mousemove', () => {
      currentTest.className = currentTest.className.replace("test2", "");
      currentTest = this;
      currentTest.className += " test2";
    });
  }); 
}
.test {
  width: 50px;
  height: 50px;
  float: left;
  cursor: pointer
}
<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="css/style.css"/>
        <script src="js/script.js" async></script>  
    </head>
    <body>     
        <div>
    <div class="test">1</div>
    <div class="test">2</div>
    <div class="test">3</div>
    <div class="test">4</div>
    <div class="test">5</div>
  </div>   
    </body>
</html>

Example - for loop (works fine):

window.onload = () => {
  test();
};


function test() {
  let testDiv = Array.from(document.getElementsByClassName("test"));
  let currentTest = testDiv[0];
  currentTest.className += " test2";
  for (var i = 0; i < testDiv.length; i++) {
   testDiv[i].onmouseover = function() {
    currentTest.className = currentTest.className.replace("test2", "");
    currentTest = this;
    currentTest.className += " test2";
   };
  }
}
.test {
  width: 50px;
  height: 50px;
  float: left;
  cursor: pointer
}
<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="css/style.css"/>
        <script src="js/script.js" async></script>  
    </head>
    <body>     
        <div>
    <div class="test">1</div>
    <div class="test">2</div>
    <div class="test">3</div>
    <div class="test">4</div>
    <div class="test">5</div>
  </div>   
    </body>
</html>
melpomene
  • 84,125
  • 8
  • 85
  • 148

2 Answers2

1

The issue has nothing to do with for vs. forEach, it's about function vs. => functions.

//                                    vvvvv
current.addEventListener('mousemove', () => {
  currentTest.className = currentTest.className.replace("test2", "");
  currentTest = this;
//              ^^^^

That this does not have the value you expect because you used an arrow function, which binds this from the surrounding context (instead of getting it passed it from the mousemove event).

To fix it, do this instead:

current.addEventListener('mousemove', function () {
  currentTest.className = currentTest.className.replace("test2", "");
  currentTest = this;

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this for details.

melpomene
  • 84,125
  • 8
  • 85
  • 148
1

This is because you are using this which is not your current element.

You need to set currentTest to current, like this:

window.onload = () => {
  test();
};


function test() {
  let testDiv = Array.from(document.getElementsByClassName("test"));
  let currentTest = testDiv[0];
  currentTest.className += " test2";
   testDiv.forEach((current) => {
    current.addEventListener('mousemove', () => {
      currentTest.className = currentTest.className.replace("test2", "");
      currentTest = current;
      currentTest.className += " test2";
    });
  }); 
}
.test {
  width: 50px;
  height: 50px;
  float: left;
  cursor: pointer;
}

.test2 {
  color: red;
}
<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="css/style.css"/>
        <script src="js/script.js" async></script>  
    </head>
    <body>     
        <div>
    <div class="test">1</div>
    <div class="test">2</div>
    <div class="test">3</div>
    <div class="test">4</div>
    <div class="test">5</div>
  </div>   
    </body>
</html>
ADreNaLiNe-DJ
  • 4,787
  • 3
  • 26
  • 35
  • I would add to the answer the main difference between arrow function's lexical context for `this` and "normal" functions with dynamic context for `this` – Sagiv b.g Feb 16 '18 at 08:11