Can someone explain this please? (solution from Eloquent JavaScript regarding eventListeners)

Hey campers,
Help would be really appreciated, I’ve been trying to figure this out for hours now.

So this was the solution to an exercise I was doing:

1. <!doctype html>

2. <tab-panel>
3.   <div data-tabname="one">Tab one</div>
4.   <div data-tabname="two">Tab two</div>
5.   <div data-tabname="three">Tab three</div>
6. </tab-panel>
7. <script>
8.   function asTabs(node) {
9.     let tabs = Array.from(node.children).map(node => {
10.       let button = document.createElement("button");
11.       button.textContent = node.getAttribute("data-tabname");
12.       let tab = {node, button};
13.       button.addEventListener("click", () => selectTab(tab));
14.       return tab;
15.     });
16.     let tabList = document.createElement("div");
17.     for (let {button} of tabs) tabList.appendChild(button);
18.     node.insertBefore(tabList, node.firstChild);

19.     function selectTab(selectedTab) {
20.       for (let tab of tabs) {
21.         let selected = tab == selectedTab;
22.         console.log(selected)
23.         tab.node.style.display = selected ? "" : "none";
24.         tab.button.style.color = selected ? "red" : "";
25.       }
26.     }
27.     selectTab(tabs[0]);
28.   }

29.   asTabs(document.querySelector("tab-panel"));
30. </script>

the part that confused me is on line 13:

13.       button.addEventListener("click", () => selectTab(tab));

Normally when adding event listeners, you wouldnt need to place a function call inside another function - it would be sufficient to say something like this:

 button.addEventListener("click", selectTab(tab));

however when I do, an error comes up saying:

ReferenceError: tabs is not defined (line 15 in function selectTab)
called from line 7 in function Array.from.map.node
called from line 3 in function asTabs
called from line 25

and that’s where I’m lost.
Thanks for your time as always

The red parentheses (the color didn’t copy for me) contain the parameters to the function. And you’re getting a reference error because the ‘tab’ variable used in the call to selectTab() doesn’t exist within the scope of the arrow function—seems like you forgot to supply the variable declared on line 12 as an argument to the arrow function.

Not sure from your post, but are you familiar with ES2015? If you’re not, this code in ES5 is functionally identical:

button.addEventListener("click", function () { selectTab(tab); });

1 Like

I’m sorry - I didn’t word my question clearly. but I’ve figured it out now!
The solution you supplied and the solution on line 13 work because .addEventListener requires a function as its second parameter.

when selectTab(tab) is used, it executes straight away, but it doesn’t return a function, so thats why an error comes up.

Not sure where it talks about this behavior in the MDN docs but it has something to do with the function having an argument.

Even if the function didn’t have an argument button.addEventListener("click", () => selectTab()); would not work either.

The only way it will work would be button.addEventListener("click", () => selectTab);

Seems you figured it out but here here is a fiddle I was playing around with to test it’s behavior.
https://jsfiddle.net/n6ccxcgq/25/

Basically it wants a function definition, not a call.

1 Like

honestly, its reasons like this why I can continue to put in hour after hour to learn how to programme.

the community and its people are absolutely amazing.

I’m really grateful for your help. may Allah bless you

1 Like