articleBody
| -
impresently:
a little confused
I know how you feel, I’m not new to JavaScript and I don’t
understand it well enough to easily explain it, but it has to do
with “closures” and “lexical scope”
I think the “Creating closures in loops: A common mistake”
section near the end of this page does the best job at explaining
that I’ve seen.
MDN Web Docs
Closures
A closure is the combination of a function and the lexical
environment within which that function was declared.
-
Hi there @impresently!
This is a tricky aspect of JavaScript, and I don’t blame you
for having trouble with it. Let’s try to clear things up.
When a function is run as a direct result of an event firing,
due to an event listener being added to the object on which the
event fires, the function implicitly has an event object available
inside its lexical scope (i.e. inside the function, but not outside
it). The reason we write e, or event, or
in this case clickedImage as a parameter is to give a
definition to this event object — i.e. a name we can refer to it
as.
So in this case, each time the loop is run, the
displayThumb() function is defined, and we add an
event listener to the newImage created on each round
of the loop that makes it so that displayThumb() is
run when the image in question is clicked on.
By the way, it would arguably be more efficient and better
practice to define the function only once, outside the loop, rather
than each time it runs (see our version at
https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/gallery/main.js),
but yours still works OK.
So the event object passed into the function each time an image
is clicked (clickedImage in your case) varies
depending on which image was clicked. It contains information
relevant to the image that was clicked.
Its target attribute contains a reference to the
image itself — “the target of the event”. This
is why when you do clickedImage.target, you can
directly access the properties available on image. We can therefore
use getAttribute('src') to access the contents of the
image’s src attribute (the path to the image),
etc.
Now, we also need to talk about the order in which things are
run around loops. When you run a loop and it has a function inside
it, the different loop iterations all run first, then the functions
inside the loop iterations will run afterwards.
This is why the second code example DOESN’T work properly. By
the time the loop iterations have all run, newImage's
src value is equal to the path to the last image. The
event listeners are all set up correctly, but the function will
always set the main image displayed to that last image.
If you instead get the src from
e.target.getAttribute(), the value will be relative to
the image clicked on, not the value of newImage after
the loops have all run, which is always equal to the last
image.
Does that help? Happy to discuss some more if you are still not
clear.
-
Hi there @impresently!
This is a tricky aspect of JavaScript, and I don’t blame you for
having trouble with it. Let’s try to clear things up.
When a function is run as a direct result of an event firing,
due to an event listener being added to the object on which the
event fires, the function implicitly has an event object available
inside its lexical scope (i.e. inside the function, but not outside
it). The reason we write e, or event, or
in this case clickedImage as a parameter is to give a
definition to this event object — i.e. a name we can refer to it
as.
So in this case, each time the loop is run, the
displayThumb() function is defined, and we add an
event listener to the newImage created on each round
of the loop that makes it so that displayThumb() is
run when the image in question is clicked on.
By the way, it would arguably be more efficient and better
practice to define the function only once, outside the loop, rather
than each time it runs (see our version at
https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/gallery/main.js),
but yours still works OK.
So the event object passed into the function each time an image
is clicked (clickedImage in your case) varies
depending on which image was clicked. It contains information
relevant to the image that was clicked.
Its target attribute contains a reference to the
image itself — “the target of the event”. This is why
when you do clickedImage.target, you can directly
access the properties available on image. We can therefore use
getAttribute('src') to access the contents of the
image’s src attribute (the path to the image),
etc.
Now, we also need to talk about the order in which things are
run around loops. When you run a loop and it has a function inside
it, the different loop iterations all run first, then the functions
inside the loop iterations will run afterwards.
This is why the second code example DOESN’T work properly. By
the time the loop iterations have all run, newImage's
src value is equal to the path to the last image. The
event listeners are all set up correctly, but the function will
always set the main image displayed to that last image.
If you instead get the src from
e.target.getAttribute(), the value will be relative to
the image clicked on, not the value of newImage after
the loops have all run, which is always equal to the last
image.
Does that help? Happy to discuss some more if you are still not
clear.
|