0

Sorry if the title isn't exactly accurate, not sure how else to describe it. I have this bit of js I pieced together using various other tutorials. It's working, but I was hoping for a little more understanding on how/why it works.

The code is for a modal on a page full of images, where you click on any image and it launches a modal with that image in it. Here are the relevant parts of the code:

function modal(modalId, modalImg) {
  var image = document.getElementsByClassName('modalImg');
  var imageLength = image.length;
  var myModal = document.getElementById(modalId);
  var modalImg = document.getElementById(modalImg);
  for (var i = 0; i < imageLength; i++) {
    (function(index) {
      image[i].onclick = function() {
        myModal.style.display = "block";
        modalImg.src = this.src;
        imgIndex = index;
      }
    })(i);
  }
}

You can see this code working (and in full context) here.

The part I'm trying to understand here involves the closure function and event listener. So the closure function is being called, and i (the for-loop's counter) is passed as the index argument. My question is, how the heck is i being set to the index of the image that was clicked?

Tyler
  • 90
  • 1
  • 7

1 Answers1

2

If we look at this code snippet:

(function(index) {
  image[i].onclick = function() {
    myModal.style.display = "block";
    modalImg.src = this.src;

    // `index` here is a variable accessible through closure scope
    imgIndex = index;
  }
})(i);

The code iterates over all images and passes each image index as i variable, which is then captured as closure inside click callback.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • Okay, so if I'm understanding you properly, the index of _italic_every_italic_ image is being passed into the closure function, then the .onclick captures the appropriate index of the individual image which becomes `this`? – Tyler Oct 15 '16 at 12:13
  • @Tyler, too many things in one comment. `(functoin(index)..)()` is not closure function, it's [IIEF](http://stackoverflow.com/questions/8228281/what-is-the-function-construct-in-javascript). The `onclick` callback captures `index` variable in closure. Inside this callback `this` points at `DOM` `` element – Max Koretskyi Oct 15 '16 at 12:17
  • Maximus: IIFE is not an official term that must be used. –  Oct 15 '16 at 12:39
  • @Tyler: The outer function you're using to wrap the code is just a plain, normal, boring function. Imagine if it was a named function declared elsewhere, and you passed all those variables into it. Would it be surprising that the event handler could read those variables? [Here's a demo](https://jsfiddle.net/rj8h7gua/) that uses a named function. So each event handler has access to `index` in the same way it has access to `modalImg` and `myModal` in your original code... via variable scope. –  Oct 15 '16 at 12:39
  • @squint, I didn't understand your comment about IIFE. It's the term used by most people to describe the behavior of immediately invoking function expression and all information about it can be googled using exactly this term. – Max Koretskyi Oct 15 '16 at 12:43
  • @Maximus, sorry, still learning the jargon for all of this. I do understand now what you mean. – Tyler Oct 15 '16 at 12:45
  • @Maximus: Yes, it's a commonly used term but I think we all understood what the OP was referring to when he called it a *"closure function"*. Let's not get too rigid with how we express ideas. It is, after all, a function that was created primarily to assist the closure created by the event handler. –  Oct 15 '16 at 12:46
  • @squint, thanks. I see that now. Also realized after messing with it a bit, that it seems completely unnecessary to use the IIEF for this particular code. I actually just tested by completely cutting the outer function and it still works just fine (unless there is some pitfall to this I don't see?) – Tyler Oct 15 '16 at 12:49
  • @squint, yeah, but it's better if one uses commonly used terms when asking questions, in this way it's faster to understand the problem. I just wanted to tell him the common term. Maybe I expressed that in a rigid manner, I don't know) – Max Koretskyi Oct 15 '16 at 12:49
  • 1
    @Tyler, there is a pitfall, check [this](http://stackoverflow.com/questions/1451009/javascript-infamous-loop-issue) – Max Koretskyi Oct 15 '16 at 12:50
  • 1
    @Tyler: The value of `i` is scoped to the outer `modal` function, and so all the event handlers you create will use that same variable if you don't create an intermediate variable scope, and therefore will all read the same value instead of the value it would have read inside the loop. –  Oct 15 '16 at 12:51
  • 1
    ...unless you use `let i` instead of `var i`, which now solves this problem by scoping `i` to the body of the `for` loop. –  Oct 15 '16 at 12:53
  • @squint, I conduct job interviews, and what I see is that many developers are not good with common terms at all. So even when asking a question, I have to first write code to show what I mean. As you said, it's certainly good to know the lingo) – Max Koretskyi Oct 15 '16 at 12:56
  • @maximus and squint I'm gonna keep the IIEF because I understand what you're saying and I want to be in the habit of doing it right. Just found it strange that when I got rid of it with this particular code, the code still worked perfectly. Regarding the terms, I do appreciate the correction. I am going to begin my first interviews for web development positions soon, and I don't want to seem utterly green. – Tyler Oct 15 '16 at 13:02
  • @Tyler, way to go) Good luck – Max Koretskyi Oct 15 '16 at 13:05