0

I have an event listener which includes an event handler with parameters ... and it works ... and, from what I have read, it is not supposed to. When I include the event handler in an anonymous function, like (I think that) I'm supposed to, it stops working.

I have searched this forum for similar issues and all of the questions are answered the same way (paraphrasing): "if you want parameters in your event handler, you have to put it in an anonymous function."

Apologies if this is a mundane question, I am new to this (and I did search for duplicate questions): But, why does this work and, more importantly, is there a better way to do this?



[Clarifying info: I have 5 buttons on a page, hence the loop. Each button controls a different area of the webpage (but with the same action, change css styling from "display:none" to "display:block") - which is why I need a one-to-one correspondence between an individual button and an individual "details" tag, hence the need for parameters in the event handler. Finally, the buttons toggle, hence the "if ... else".]


p.s I have a put a page online, temporarily, so you can see how it works (it is just a "notes to myself" page and is incomplete) : http://www.mylescallan.com/gameDesign/gameDesignIntroduction.html


var buttons = document.getElementsByClassName("expand"),
    details = document.getElementsByClassName("reveal"),
    i;

function makeClickHandler(details, i) {
    "use strict";
    function myHandler() {
    if (details[i].style.display === 'block') {
        details[i].style.display = 'none';
        buttons[i].innerHTML = "<em>Click Here:</em> To Expand The Source  Code For This Section";
        buttons[i].style.opacity = "1";
    } else {
        details[i].style.display = 'block';
        buttons[i].innerHTML = "<em>Click Here<em>: Don't Forget To Hide This Section, You've Read It";
        buttons[i].style.opacity = "0.5";
    }
};
    return myHandler;
}

for ( i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", makeClickHandler(details, i), false);
}
myles
  • 175
  • 1
  • 3
  • 9
  • [Another stack overflow question](http://stackoverflow.com/questions/28054692/can-i-pass-more-arguments-into-event-handler-functions-in-javascript) has the answer that I was looking for. The function "myHandler" was unnecessary in my code, and I could have replaced "function myHandler(){...}" with "return function(evt){ ...}" ... still not 100% sure why this works. But I'm getting closer :D – myles May 08 '15 at 03:32

1 Answers1

1

if you want parameters in your event handler, you have to put it in an anonymous function.

Not exactly. If you want iteration-dependent parameters in your event handler, you will have to put the handler in its own scope where those parameters are stored.

Now, this scope is often achieved by using an IEFE (see here for examples), which typically is anonymous. However, you can also name them without effecting the handler behaviour.

In your example snippet, none of the functions are anonymous, they are very explicitly named. makeClickHandler() does provide the scope with the i variable, in which the myHandler closure lives. It works, as is expected.

Maybe it helps your understanding when you notice that

function makeClickHandler(details, i) {
    return function myHandler(event) {
        … // use details, i, event
    };
}
for (var i = 0; i < buttons.length; i++)
    buttons[i].addEventListener("click", makeClickHandler(details, i) , false);
//                                                      no call here ^

is equivalent to

function addClickHandler(button, details, i) {
    function myHandler(event) {
        … // use details, i, event
    }
    button.addEventListener("click", myHandler , false);
//                               no call here ^ (that's what is said everywhere)
}
for (var i = 0; i < buttons.length; i++)
    addClickHandler(buttons[i], details, i);
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for your reply! However, the loop is a "red herring" here (it is why I put the closing function in my handler). My question is solely about parameters in event handlers in an event listener. Every book that I have read (am reading) and many replies to questions on this forum, say explicitly that you should put an anonymous function in the event listener, if you want parameters in your event handler. I'm wondering whether all that advice can be safely ignored, and whether there is a better way to write my code. – myles May 07 '15 at 19:41
  • What do you mean by "paramters"? If you want to receive the `Event` argument to your listener function, you just have to put a parameter there: `function myHandler(event) { … }`. Whether it is named or not does never matter. You always have to pass a function of course (named or anonymous function expression, or one declared elsewhere) - what else would you pass? – Bergi May 07 '15 at 22:44
  • makeClickHandler(details, i) – myles May 08 '15 at 01:06
  • makeClickHandler(details, i) - "details" and "i" are parameters of the function makeClickHandler. I have them as parameters of the makeClickHandler function in my event listener. Which, according to everything that I have read, you are not supposed to (I am currently reading Eloquent Javascript and a book by John Duckett). (For example, you say that event is a parameter of the event handler, which is obvious, but you don't include that when you attach the event handler to the event listener: item.addEventListener("click", clickHandler, false);, for example). – myles May 08 '15 at 01:14
  • Ah, now I understand what you're getting at :-) The parameters for `makeClickHandler` are fine, because they are no parameters to your event handler (which is `myEventHandler`). What would be wrong is `addEventListener("click", myEventHandler(event))` - it must be `addEventListener("click", myEventHandler)`, you have to pass a function. That function can be instantiated in place using an (anonymous) function expression, it can a reference to a function declared elsewhere, or it can be the return value of a function call (`makeClickHandler(…)`). The latter can be confusing though. – Bergi May 08 '15 at 02:27
  • I think that we are going "around the houses" on this one. But I have a feeling that we will get there in the end :D. Here is where I attach my event handler to my event listener: buttons[i].addEventListener("click", makeClickHandler(details, i), false); makeClickHandler is my event handler, which includes parameters. (I am not sure what you are referring to when you say that myEventHandler is my "event handler" as there is no "myEventHandler" ... unless you are saying that "myHandler" is my actual "event handler" and makeClickHandler is simply taking the place of an anonymous function?) – myles May 08 '15 at 02:43
  • Uh, right, I meant `myHandler`. That's exactly what I tried to say - `makeClickHandler` is not your event handler, `makeClickHandler(details, i)` is. And you're not passing arguments to your event handler here. – Bergi May 08 '15 at 02:48
  • Your last comment appears to be contradictory ... can't make head nor tail of it. Sorry. I'm obviously passing arguments in makeClickHandler(details, i). And if it is my event handler, everything that I read tells me that I shouldn't. Do you know of any good links to help? The readings that I have, and the searches that I have done, all say the same thing: "you can't pass arguments to your event handler when attaching it to an event listener" .. but I have, and it worked. And I am still confused :D Appreciate your patience with this, any resources would be appreciated!! Thanks!! – myles May 08 '15 at 03:07
  • `makeClickHandler` is, as its name says, a function that returns your handler function - it's not the event handler itself. Maybe have a look at [How do JavaScript closures work?](http://stackoverflow.com/q/111102/1048572) – Bergi May 08 '15 at 03:47
  • You are passing arguments to `makeClickHandler` in the call `makeClickHandler(details, i)`. But you're not passing arguments to the result of `makeClickHandler(details, i)` - the `myHandler` function is not called. – Bergi May 08 '15 at 03:49
  • :D Thanks for your help. I'll sleep on it!! – myles May 08 '15 at 04:02
  • p.s. just noticed your edit ... haven't taken it in fully yet but the equivalence appears to be the part that I was missing. Thanks again!! (Just noticed, you first answered this 11 hours ago ... hopefully I get the chance to "pay it forward' .... provided I get the hang of it!! ) – myles May 08 '15 at 04:07