0

I want to pass arguments to an event listener in Javascript. I have found solutions however I cannot understand why or how they work and why other solutions do not work.

I have a C/C++ background, however in Javascript functions perform a lot different. Could you please help me understand how and why the following examples work or not work?

Without parameters the code works fine:

var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction);

function eventFunction()
{
  console.log("works");
}

If I include brackets on the eventListener the function executes once without the event firing and then does nothing:

var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction());

function eventFunction()
{
  console.log("works");
}

I suppose this is because the function is invoked at the eventListener and this doesn't allow the eventListener function to call the eventFunction later.


When I want to pass an argument with the eventFunction. The following does not work:

var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction("works"));

function eventFunction(myStr)
{
    console.log(myStr);
}

It invokes the eventFunction without the event firing and then when the events fires does nothing which is the same behaviour as previously. So far I think I can understand.


To pass the argument correctly in the event function I found the following code. The following solution works fine:

var clicker = document.getElementById("mainNavToggle");
clicker.addEventListener("click", eventFunction("works"));

function eventFunction(myStr)
{
  function func(event, myStr)
  {
    console.log(myStr);
  }

  return function(event) { 
    func(event,myStr) 
  };
}

The first thing I notice is that this eventFunction is not invoked immediately. Why is that?

However, I am at a loss of how arguments are passed within each function. I understand that the "works" literal is passed to myStr on the eventFunction. However in the function func there is an additional event parameter not passed by eventFunction.

Also why does the eventFunction needs to return another function with one parameter that internally calls the func function?

Also please notice that I want to pass parameters to the eventFunction that do not have a global scope, otherwise there would not be a need to pass parameters.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Parakmi I
  • 25
  • 6
  • 1
    Possible duplicate of [How to pass arguments to addEventListener listener function?](https://stackoverflow.com/questions/256754/how-to-pass-arguments-to-addeventlistener-listener-function) – Dexygen May 23 '19 at 12:52
  • I am inclined to say this question is different because it is asking more about "why", whereas the other question is focused on "how". The question asker arrived knowing how already. @GeorgeJempty – AlexMA May 23 '19 at 12:57

2 Answers2

5

Let's dissect this step-by-step.

General information

The .addEventListener function/method takes in 2 (or 3) arguments:

  • a String, with a value of the event type/name (for example "click")
  • a pointer to a Function, which should be executed when the event occurs
  • a Boolean, specifying if event bubbling or event propagation should be used. This argument is optional and can be omitted.

Example 1

In you first example, you pass a String ("click") and a pointer to a Function (eventFunction) into .addEventListener. Everything works as expected, the event occurs and the function is executed.

Example 2

In this example, you pass a String and undefined to .addEventListener, which is not what .addEventListener expects. You might ask yourself "When did I pass in undefined?". Well, it happened as soon as you executed your eventFunction by adding parens () after it. Here, the syntax in JS is equal to the syntax in most other languages, adding parens to the right side of a function executes the function.

Because your eventFunction doesn't return anything explicitly, it automatically returns undefined. Therefor, this is what .addEventListener got:

clicker.addEventListener("click", undefined)

However, because you executed eventFunction, it logs "It worked" to the console once.

Example 3

This example fails for the same reasons example 2 failed.

Example 4

The code in the last example works, because it uses a concept called closure. There are literally thousands of explanations of the concept, so my explanation here will be really short. If you have trouble understanding it, just google for "javascript closures".

In contrast to a lot of other languages, JavaScript has function scoping instead of block scoping. So if you define a variable inside a function F and return another function G from within F, G will have access to variables and arguments from inside F. For demonstration purposes:

function F () {
  var fVariable = 'Defined in F';
  return function G () {
    return fVariable; // <-- this is the closure
  }
}

var funcG = F(); // <-- execute F
funcG(); // -> 'Defined in F';

Compare this short example with your fourth code: They are pretty much the same. The only difference is, that your code creates an extra function func.

Now, if you call clicker.addEventListener("click", eventFunction("it works")), you execute eventFunction, which returns another (anonymous/lambda) function which encapsulates the "it works" string via a closure. If we write it out by hand, this is what .addEventListener "sees":

clicker.addEventListener("click", function (event) {
  func("it works", event);
});

EDIT: Solving the problem

Here's a working example, note that I changed the function names to reflect their purpose:

function eventHandlerFactory (myStr) { // <-- eventFunction in your code
  return function executeOnEvent (event) { // <-- executed if an event occurs
    console.log(myStr);
    console.log(event);
  }
}

clicker.addEventListener("click", eventHandlerFactory("it worked"));

The eventHandlerFactory function returns a function executeOnEvent, which is passed into .addEventListener. Writing it out by hand again, this is what the interpreter understands:

clicker.addEventListener("click", function executeOnEvent (event) {
  console.log("it works");
  console.log(event);
});
David
  • 3,552
  • 1
  • 13
  • 24
  • Thank you for a very detailed explanation. So from within the function eventFunc we return a pointer to an anonymous function which takes one argument (event) and executes the func function which has access to the myStr string due to closure. Following your logic i thought it might work if i return a pointer to func directly instead of a pointer to an undefined function which calls func. So i tried `function eventFunction(myStr) { function func(event,myStr) {console.log(myStr);} return func(event,myStr); }` – Parakmi I May 23 '19 at 15:09
  • Continuing.. what i get now as output is the event object as if the function func contains `console.log (event)` why is that? – Parakmi I May 23 '19 at 15:11
  • ..conitnuing. After some thought i think it has something to do with the type of pointer to the function the eventlistener expects. Could it be that it expects only one argument function pointer and that argument is always the event so it makes my second argument event as well????! – Parakmi I May 23 '19 at 15:13
  • @ParakmiI (1) Updated my answer to include a working example. (2) In regards to your last question: The event handler function (see updated code above) always receives the `Event` object implicitly. And yes, the `.addEventListener` function expects a function with an arity of 1. That's why we used the closure (although there are other ways to do it, like _partial application_). – David May 24 '19 at 07:40
0

The first thing i notice is that this eventFunction is not invoked immediately

It is invoked immediately. However, it returns a new function. This is the function that is registered for the click handler. This function, when called, calls the function func in a closure. The arguments passed to this function are also preserved in a closure.

To understand this concept, you need to understand how closures work. This is a topic that has been written about extensively, so I'll just point you to How do JavaScript closures work?.

AlexMA
  • 9,842
  • 7
  • 42
  • 64