0

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.

DevKLiD
  • 89
  • 1
  • 1
  • 7
  • for event listner you should pass a method refrence.in second one you do it right.but in the first case you are passing undefined (what returns from the pickSelection method call) .first one should be `function(){pickSelection(element)}` – Madhawa Priyashantha Mar 02 '19 at 18:38

3 Answers3

2

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>
Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44
  • I understand now, thanks so much! But another question I have, in the second version when it says: "() => pickSelection(element)", shouldn't the empty parentheses have a parameter inside of them? Because in arrow function syntax doesn't () => mean the function takes no arguments? But in the case of the pickSelection function, it does require one argument. – DevKLiD Mar 02 '19 at 19:11
  • The arrow function `=>` is actually the *callback* which is called when the event happens, which in turn will call the `pickSelection` function and pass the `element` to it. It typically should take one argument which is the `event` object, like so: `(event) => pickSelection(element);` which is *optional*. But since you are already passing the `element` local variable to `pickSelection` it as working as expected. – Fullstack Guy Mar 02 '19 at 19:24
0

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>
gman
  • 100,619
  • 31
  • 269
  • 393
0

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>
marzelin
  • 10,790
  • 2
  • 30
  • 49