54

So we have a page:

<span id='container'>
    <a href='#' id='first'>First Link</a>
    <a href='#' id='second'>Second Link</a>
</span>

And want to add some click events:

first.addEventListener('click', function(){alert('sup!');})

Works like a charm! However, when you make the second argument an external function:

function message_me(m_text){
    alert(m_text)
}

second.addEventListener('click', message_me('shazam'))

It calls the function immediately. How can I stop this? So annoying!

Here's a live demo: http://jsfiddle.net/ey7pB/1/

LittleBobbyTables
  • 4,361
  • 9
  • 38
  • 67
  • 7
    Since the second parameter expects a function **reference**, you need to provide one. With your problematic code, you're immediately calling the function and passing its result (which is *undefined*). Either call the function in an anonymous function (like your first example) or alter the function to **return** a function (probably not ideal). – Ian Apr 30 '13 at 23:17
  • 2
    Why is this not ideal? It seems against D.R.Y. to copypasta my function in the 4 or so addEventListeners that im setting, no? – LittleBobbyTables Apr 30 '13 at 23:20
  • Another option is to store the required message as an attribute on the element, then bind the function as `second.addEventListener('click', message_me)` and have it retrieve the message from the attribute rather than from a parameter. – nnnnnn Apr 30 '13 at 23:23
  • 2
    Possible duplicate of [How to pass parameter to function using in addEventListener?](http://stackoverflow.com/questions/12024483/how-to-pass-parameter-to-function-using-in-addeventlistener) – Sebastian Simon Jun 20 '16 at 03:46

5 Answers5

46

Quoting Ian's answer:

Since the second parameter expects a function reference, you need to provide one. With your problematic code, you're immediately calling the function and passing its result (which is undefined...because all the function does is alert and doesn't return anything). Either call the function in an anonymous function (like your first example) or alter the function to return a function.

function message_me(m_text){
    alert(m_text)
} 

second.addEventListener('click', 
    function() {
        message_me('shazam');
    }
);

Here's an updated fiddle.

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
clav
  • 4,221
  • 30
  • 43
28

Since the second parameter expects a function reference, you need to provide one. With your problematic code, you're immediately calling the function and passing its result (which is undefined...because all the function does is alert and doesn't return anything). Either call the function in an anonymous function (like your first example) or alter the function to return a function.

You can do this:

function message_me(m_text){
    alert(m_text);
}

second.addEventListener('click', function () {
    message_me('shazam')
});

or this:

function message_me(m_text){
    return function () {
        alert(m_text);
    };
}

second.addEventListener('click', message_me('shazam'));

DEMO: http://jsfiddle.net/tcCvw/

Ian
  • 50,146
  • 13
  • 101
  • 111
21

or you can use .bind

function message_me(m_text){
    alert(m_text);
}

second.addEventListener('click', message_me.bind(this, 'shazam'));

check MDN Documentation about 'closures'

avseoul
  • 246
  • 2
  • 4
  • 2
    +1 binding should be the way to go — not sure if that was available 4 years ago, but is definitely the preferred way to today, as opposed to creating an anonymous function – vol7ron Mar 07 '17 at 00:40
  • 1
    Nice suggestion for the bind, definitely should be the accepted answer. Can OP change? – blueprintchris May 31 '18 at 10:50
  • 1
    @vol7ron "*as opposed to creating an anonymous function*" `.bind()` still creates a new function. I don't see why it would be preferable. Moreover, it will *always* set the `this` value, which means that if the handler expects it to be the element, then `.bind()` will break that. It's definitely no silver bullet. – VLAZ Apr 13 '22 at 11:27
  • 2
    @VLAZ you're bringing me out of retirement ;) There are a few things here. (1) at the time of this comment arrow functions `()=>{...}` and ES6 was still relatively new and also discouraged because IE had not supported it. (2) the anonysmous function was literally the longer `function(){...}` syntax, which was less readable. (3) While it may be slower to construct the underlying function, calls to the `bind()` function are significantly faster (1.5-2X), which may matter in a loop. I acknowledge your point about `this` being forcibly set :) The beatufiul thing about JS is options. – vol7ron Apr 21 '22 at 23:23
17

Modern ES6 solution using arrow functions

second.addEventListener('click', () => message_me('shazam'))
Jesse Reza Khorasanee
  • 3,140
  • 4
  • 36
  • 53
3

Year 2022

I know the following possibilities:

option 1 (anonymous function)

function message_me(m_text){
    alert(m_text);
}

second.addEventListener('click', function () {
    message_me('shazam')
});

option 2 (callback fn calls a annoymous fn)

function message_me(m_text){
    return function () {
        alert(m_text);
    };
}

second.addEventListener('click', message_me('shazam'));

option 3 (bind())

function message_me(m_text){
    alert(m_text);
}

second.addEventListener('click', message_me.bind(this,'shazam'));

option 4 (arrow fn)

function message_me(m_text){
    alert(m_text);
}

second.addEventListener('click', () => {
    message_me('shazam')
});

I personally like to use the (option 4) arrow function because you get the context included. Another nice solution is the bind (option 3). What bothers me a bit about this variant is that you have to explicitly include the context with this. That can easily be forgotten. Option 1 is fine and this may have been the standard way in the past. To option 2 I can only say: it works but for my taste it looks awkward.

But basically you have to take what you find most understandable from the syntax. Because in the end you have to remember so much, then make your life easier and take what you can remember best. In my case that is option 4 and option 3 then option 1 and option 2 is not an alternative for me.

Maik Lowrey
  • 15,957
  • 6
  • 40
  • 79