-1

i realise this is probably classed as a duplicate however I've read this question and the replies in detail but still can't get my head around it.

I have this code, but within the 'focus' function, 'i' is always 2, (the pageSearchInput.length value), and I want it to be 0,1 respectively on each loop.

I understand that this is a scoping issue but I can't work out how to fix this without simply using 'let'.

If someone could explain how this works i'd be very grateful.

for (i = 0; i < pageSearchInput.length; i++) {

    pageSearchInput[i].addEventListener("focus", function(){

        pageSearchContainer[i].style.outline = "4px solid #ffcc33";
        pageSearchButton[i].style.backgroundColor = "#008920";
        pageSearchButton[i].style.border = "1px solid #008920";

    });

}
paddyfields
  • 1,466
  • 2
  • 15
  • 25
  • Im not sure, to be honest, but maybe define `var i` outside of the loop? – Ahorn Aug 09 '18 at 11:50
  • @ArthurWietzorek it may be defined outside the loop already? – OliverRadini Aug 09 '18 at 11:51
  • Why __wouldn't__ you just use `let`? This is one of `let`'s main reasons for existing. – Cerbrus Aug 09 '18 at 11:54
  • Because it isn't supported on IE10 or lower, and the website I'm making needs to. This question has been marked as duplicate for a 'let' question, when I've explicitly said I don't want to use it in the question. Slightly trigger happy – paddyfields Aug 09 '18 at 11:56
  • It's because `i` is incremented until it is `pageSearchInput.length - 1` and the loop terminates. *Then* at some point in the future the event listener is executed, using the value of `i` *at that point in time`. Wrap the code inside in a function that takes a parameter and pass in `i` and it will execute as expected. – Reinstate Monica Cellio Aug 09 '18 at 11:56
  • @paddyfields: I've updated the target. There are a bazillion questions out there that discuss this subject. I'm not trigger-happy. Maybe you were by posting this question? – Cerbrus Aug 09 '18 at 11:57
  • Not particularly, I'm new to javascript and it's not always easy to know the terminology you need to search for. Thank you for the link, it's clearly what I needed to be reading. – paddyfields Aug 09 '18 at 12:02
  • You're welcome. But as a note, next time, please refrain from calling someone "trigger-happy". It's not very nice. If you disagree, feel free to ask the user why he did what he did, though. – Cerbrus Aug 09 '18 at 12:02
  • 2
    Sure thing, thanks again. – paddyfields Aug 09 '18 at 12:05

5 Answers5

1

Just make i a parameter to the function and pass it, and return the function that handles the event:

for (i = 0; i < pageSearchInput.length; i++) {

    pageSearchInput[i].addEventListener("focus", (function(i){
      return function() {
        pageSearchContainer[i].style.outline = "4px solid #ffcc33";
        pageSearchButton[i].style.backgroundColor = "#008920";
        pageSearchButton[i].style.border = "1px solid #008920";
      };
    })(i));

}
Jared Smith
  • 19,721
  • 5
  • 45
  • 83
1

If you don't want to use let you should create a wrapping function.

for (i = 0; i < pageSearchInput.length; i++) {


    pageSearchInput[i].addEventListener("focus", function(){
        return (function(i) {
            pageSearchContainer[i].style.outline = "4px solid #ffcc33";
            pageSearchButton[i].style.backgroundColor = "#008920";
            pageSearchButton[i].style.border = "1px solid #008920";
        })(i);
    });

}
Tzook Bar Noy
  • 11,337
  • 14
  • 51
  • 82
1

You can use IIFE (Immediately Invoked Function Expression)

(function () {
  statements
})();

It is a design pattern which is also known as a Self-Executing Anonymous Function and contains two major parts. The first is the anonymous function with lexical scope enclosed within the Grouping Operator (). This prevents accessing variables within the IIFE idiom as well as polluting the global scope.

The second part creates the immediately executing function expression () through which the JavaScript engine will directly interpret the function.

With IIFE your code should be:

for (i = 0; i < pageSearchInput.length; i++) {
  (function(i){
     pageSearchInput[i].addEventListener("focus", function(){

       pageSearchContainer[i].style.outline = "4px solid #ffcc33";
       pageSearchButton[i].style.backgroundColor = "#008920";
       pageSearchButton[i].style.border = "1px solid #008920";

    });
  })(i);
}
Mamun
  • 66,969
  • 9
  • 47
  • 59
0

You have to use let keyword in order to preserve the value of i in the for loop scope.

for (let i = 0; i < pageSearchInput.length; i++) {

Another method is to use Immediately-invoked function expression

(function(i){
   pageSearchInput[i].addEventListener("focus", function(){

   });
})(i);
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
0

You can try to use IIFE to get your problem fixed related to scope.

for (i = 0; i < pageSearchInput.length; i++) {
   (function(j){
    return pageSearchInput[j].addEventListener("focus", function(){

        pageSearchContainer[j].style.outline = "4px solid #ffcc33";
        pageSearchButton[j].style.backgroundColor = "#008920";
        pageSearchButton[j].style.border = "1px solid #008920";

    })
  }(i))

}

For details find the explanation in this post http://learnwebtechs.com/2018/05/11/understanding-javascript-closures-with-example

KrishnaSingh
  • 696
  • 1
  • 7
  • 12