Reputation: 159
I have a list of elements in my HTML that looks like this:
<div class='container'>
<div class="zone green">🦊</div>
<div class="zone red">🐰</div>
<div class="zone blue">🐸</div>
<div class="zone yellow">🦁</div>
<div class="zone purple">🐯</div>
<div class="zone brown">🐭</div>
<div class="zone green">🦄</div>
<div class="zone red">🐲</div>
<div class="zone blue">🐷</div>
<div class="zone yellow">🐺</div>
<div class="zone purple">🐼</div>
<div class="zone brown">🐻</div>
<script type="text/javascript" src="script.js"></script>
</div>
And I was trying to make it so that every element I clicked on would log to the console the animal in the element. This was my attempt:
const selection = document.querySelectorAll(".zone");
selection.forEach(element => {
element.addEventListener('click', pickSelection(element))
})
function pickSelection(animal) {
console.log(animal.textContent)
}
But it was not returning anything when I clicked on any of the elements. However, once I changed the eventListener to this, it started working:
selection.forEach(element => {
element.addEventListener('click', () => pickSelection(element))
})
Why does it work in the second version of the code and not the first? In the first version, I thought I am passing the element argument to the pickSelection function by writing "pickSelection(element)", but apparently it only works if there is "() =>" in front of it, but what is the difference in this notation? Thanks.
Upvotes: 0
Views: 863
Reputation: 11600
Sidenote: Registering a listener on each element is a bad practice. Use event delegation instead:
document.querySelector(`.container`).addEventListener(`click`, ({target}) => {
const el = target.closest(`.zone`);
if (el) {
console.log(el.textContent);
}
})
<div class='container'>
<div class="zone green">🦊</div>
<div class="zone red">🐰</div>
<div class="zone blue">🐸</div>
<div class="zone yellow">🦁</div>
<div class="zone purple">🐯</div>
<div class="zone brown">🐭</div>
<div class="zone green">🦄</div>
<div class="zone red">🐲</div>
<div class="zone blue">🐷</div>
<div class="zone yellow">🐺</div>
<div class="zone purple">🐼</div>
<div class="zone brown">🐻</div>
</div>
Upvotes: 0
Reputation:
This line
element.addEventListener('click', pickSelection(element));
Is effectively this
const temp = pickSelection(element);
element.addEventListener('click', temp);
What you want is
element.addEventListener('click', pickSelection);
Do you see the difference?
In this version
element.addEventListener('click', pickSelection(element))
You called the function pickSelection
and passed it element
. Then passed whatever pickSelection
returns to element.addEventListener
What you wanted / needed to do is pass the function itself to element.addEventListener
The reason this version works
selection.forEach(element => {
element.addEventListener('click', () => pickSelection(element))
})
is because you're passing an anonymous function to element.addEventListener
. Those lines can be translated to this
function anonymousFn1(element) {
function anonymousFn2() {
pickSelection(element);
}
element.addEventListener('click', anonymousFn2);
}
selection.forEach(anonymousFn1);
Note that functionally there is another difference.
In this one
element.addEventListener('click', pickSelection);
The click event passes an Event object to pickSelection
whereas in the other one the variable element
that was created when forEach
called anonmousFn1 was "closed over" (a closure was made) so when anonymousFn2 was call that element
variable was use.
So to get the first one to work you really need this
function pickSelection(event) {
const element = event.target;
console.log(element.textContent);
}
Example:
const selection = document.querySelectorAll(".zone");
selection.forEach(element => {
element.addEventListener('click', pickSelection);
});
function pickSelection(event) {
element = event.target;
console.log(element.textContent);
}
<div class='container'>
<div class="zone green">🦊</div>
<div class="zone red">🐰</div>
<div class="zone blue">🐸</div>
<div class="zone yellow">🦁</div>
<div class="zone purple">🐯</div>
<div class="zone brown">🐭</div>
<div class="zone green">🦄</div>
<div class="zone red">🐲</div>
<div class="zone blue">🐷</div>
<div class="zone yellow">🐺</div>
<div class="zone purple">🐼</div>
<div class="zone brown">🐻</div>
</div>
Upvotes: 0
Reputation: 16908
In the first version you are executing the pickSelection
function instead of passing its reference, the addEventListener
expects a function reference as a callback when the particular event is triggered.
But since you passed return value of the pickSelection
function which is undefined
(as you are not returning anything from the pickSelection
so by default it is returning undefined
) it is not working.
In the second version you are actually passing the function reference to the addEventListner
, which being a arrow function syntax.
This following would also work, by simply passing the reference pickSelection
, but this time it will receive the event
object.
const selection = document.querySelectorAll(".zone");
selection.forEach(element => {
element.addEventListener('click', pickSelection)
})
function pickSelection(event) {
//getting the event object from the callback, target refers to the current element clicked.
console.log(event.target.textContent)
}
<div class='container'>
<div class="zone green">🦊</div>
<div class="zone red">🐰</div>
<div class="zone blue">🐸</div>
<div class="zone yellow">🦁</div>
<div class="zone purple">🐯</div>
<div class="zone brown">🐭</div>
<div class="zone green">🦄</div>
<div class="zone red">🐲</div>
<div class="zone blue">🐷</div>
<div class="zone yellow">🐺</div>
<div class="zone purple">🐼</div>
<div class="zone brown">🐻</div>
</div>
Upvotes: 2