Abhishek Yadav
Abhishek Yadav

Reputation: 5251

How to pass arguments to addEventListener listener function?

The situation is somewhat like-

var someVar = some_other_function();
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);

The problem is that the value of someVar is not visible inside the listener function of the addEventListener, where it is probably being treated as a new variable.

Upvotes: 507

Views: 866315

Answers (30)

John
John

Reputation: 11429

In case you want to remove the event-listener later, creating a reference to a currying function is a good alternative.

In the code below, I will illustrate what I mean.

// This is the curry function. We return a new function with the signature of what the click-listener expects
const handleClick = (foo, bar) => (clickEvent) => {
  console.log('we get our custom input', foo, bar);
  console.log('we get the click event too', clickEvent);
}

// We need to store a reference to the listener, making sure we are removing the correct reference later
const myListener = handleClick('foo', 'bar'); // Remember that we now return the actual event-handler

const btn = document.getElementById('btn'); // find the element to attach the listener to
btn.addEventListener('click', myListener);

// remove the event listener like this by using our reference
btn.removeEventListener('click', myListener);

Here's a working example on CodePen

Upvotes: 4

Dejan Dozet
Dejan Dozet

Reputation: 1019

The PERFECT SOLUTION for this is to use Closures like this:

function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

//pass parameters here and keep the reference in variables:
const size12 = makeSizer(12);
const size24 = makeSizer(24);
const size36 = makeSizer(36);

document.getElementById('size-12').addEventListener("click", size12);
document.getElementById('size-24').addEventListener("click", size24);
document.getElementById('size-36').addEventListener("click", size36);

document.getElementById('remove-12').addEventListener("click", ()=>{
    document.getElementById('size-12').removeEventListener("click", size12);
  alert("Now click on 'size 12' button and you will see that there is no event listener any more");
});
test<br/>
<button id="size-12">
size 12
</button>

<button id="size-24">
size 24
</button>

<button id="size-36">
size 36
</button>

<button id="remove-12">
remove 12
</button>

So basically you wrap a function inside another function and assign that to a variable that you can register as an event listener, but also unregister as well!

Upvotes: 2

qpaycm
qpaycm

Reputation: 930

Another workaround is by Using data attributes

function func(){
    console.log(this.dataset.someVar);
    div.removeEventListener("click", func);
}
    
var div = document.getElementById("some-div");
div.setAttribute("data-some-var", "hello");
div.addEventListener("click", func);

jsfiddle

Upvotes: 3

leafsAsd
leafsAsd

Reputation: 21

I suggest you to do something like that:

var someVar = some_other_function();
someObj.addEventListener("click", (event, param1 = someVar) => {
    some_function(param1);
}, false);

Upvotes: 2

Alvaro Gabriel Gomez
Alvaro Gabriel Gomez

Reputation: 141

one easy way to execute that may be this

    window.addEventListener('click', (e) => functionHandler(e, ...args));

Works for me.

Upvotes: 14

Brian Moore
Brian Moore

Reputation: 1698

This question is old but I thought I'd offer an alternative using ES5's .bind() - for posterity. :)

function some_func(otherFunc, ev) {
    // magic happens
}
someObj.addEventListener("click", some_func.bind(null, some_other_func), false);

Just be aware that you need to set up your listener function with the first param as the argument you're passing into bind (your other function) and the second param is now the event (instead of the first, as it would have been).

Upvotes: 131

Michael
Michael

Reputation: 39

Since your event listener is 'click', you can:

someObj.setAttribute("onclick", "function(parameter)");

Upvotes: 3

AAYUSH SHAH
AAYUSH SHAH

Reputation: 131

I have very simplistic approach. This may work for others as it helped me. It is... When you are having multiple elements/variables assigned a same function and you want to pass the reference, the simplest solution is...

function Name()
{

this.methodName = "Value"

}

That's it. It worked for me. So simple.

Upvotes: -1

hoogw
hoogw

Reputation: 5535

In 2019, lots of api changes, the best answer no longer works, without fix bug.

share some working code.

Inspired by all above answer.

 button_element = document.getElementById('your-button')

 button_element.setAttribute('your-parameter-name',your-parameter-value);

 button_element.addEventListener('click', your_function);


 function your_function(event)
   {
      //when click print the parameter value 
      console.log(event.currentTarget.attributes.your-parameter-name.value;)
   }

Upvotes: 7

Zaloz
Zaloz

Reputation: 5541

Why not just get the arguments from the target attribute of the event?

Example:

const someInput = document.querySelector('button');
someInput.addEventListener('click', myFunc, false);
someInput.myParam = 'This is my parameter';
function myFunc(evt)
{
  window.alert(evt.currentTarget.myParam);
}
<button class="input">Show parameter</button>

JavaScript is a prototype-oriented language, remember!

Upvotes: 541

Bruce Tong
Bruce Tong

Reputation: 1431

just would like to add. if anyone is adding a function which updates checkboxes to an event listener, you would have to use event.target instead of this to update the checkboxes.

Upvotes: 0

Spoo
Spoo

Reputation: 215

Probably not optimal, but simple enough for those not super js savvy. Put the function that calls addEventListener into its own function. That way any function values passed into it maintain their own scope and you can iterate over that function as much as you want.

Example I worked out with file reading as I needed to capture and render a preview of the image and filename. It took me awhile to avoid asynchronous issues when utilizing a multiple file upload type. I would accidentally see the same 'name' on all renders despite uploading different files.

Originally, all the readFile() function was within the readFiles() function. This caused asynchronous scoping issues.

    function readFiles(input) {
      if (input.files) {
        for(i=0;i<input.files.length;i++) {

          var filename = input.files[i].name;

          if ( /\.(jpe?g|jpg|png|gif|svg|bmp)$/i.test(filename) ) {
            readFile(input.files[i],filename);
          }
       }
      }
    } //end readFiles



    function readFile(file,filename) {
            var reader = new FileReader();

            reader.addEventListener("load", function() { alert(filename);}, false);

            reader.readAsDataURL(file);

    } //end readFile

Upvotes: 0

Gon82
Gon82

Reputation: 509

nice one line alternative

element.addEventListener('dragstart',(evt) => onDragStart(param1, param2, param3, evt));
function onDragStart(param1, param2, param3, evt) {

 //some action...

}

Upvotes: 50

chovy
chovy

Reputation: 75804

    $form.addEventListener('submit', save.bind(null, data, keyword, $name.value, myStemComment));
    function save(data, keyword, name, comment, event) {

This is how I got event passed properly.

Upvotes: 8

developer82
developer82

Reputation: 13733

If I'm not mistaken using calling the function with bind actually creates a new function that is returned by the bind method. This will cause you problems later or if you would like to remove the event listener, as it's basically like an anonymous function:

// Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', myCallback);
someObject.removeEventListener('event', myCallback);

// Not Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', function() { myCallback });
someObject.removeEventListener('event', /* can't remove anonymous function */);

So take that in mind.

If you are using ES6 you could do the same as suggested but a bit cleaner:

someObject.addEventListener('event', () => myCallback(params));

Upvotes: 9

Oleksandr Tkalenko
Oleksandr Tkalenko

Reputation: 1108

You can just bind all necessary arguments with 'bind':

root.addEventListener('click', myPrettyHandler.bind(null, event, arg1, ... ));

In this way you'll always get the event, arg1, and other stuff passed to myPrettyHandler.

http://passy.svbtle.com/partial-application-in-javascript-using-bind

Upvotes: 47

fnclovers
fnclovers

Reputation: 127

This solution may good for looking

var some_other_function = someVar => function() {
}

someObj.addEventListener('click', some_other_function(someVar));

or bind valiables will be also good

Upvotes: -2

Victor Behar
Victor Behar

Reputation: 848

You need:

newElem.addEventListener('click', {
    handleEvent: function (event) {
        clickImg(parameter);
    }
});

Upvotes: 0

ahuigo
ahuigo

Reputation: 3331

You could pass somevar by value(not by reference) via a javascript feature known as closure:

var someVar='origin';
func = function(v){
    console.log(v);
}
document.addEventListener('click',function(someVar){
   return function(){func(someVar)}
}(someVar));
someVar='changed'

Or you could write a common wrap function such as wrapEventCallback:

function wrapEventCallback(callback){
    var args = Array.prototype.slice.call(arguments, 1);
    return function(e){
        callback.apply(this, args)
    }
}
var someVar='origin';
func = function(v){
    console.log(v);
}
document.addEventListener('click',wrapEventCallback(func,someVar))
someVar='changed'

Here wrapEventCallback(func,var1,var2) is like:

func.bind(null, var1,var2)

Upvotes: 19

tomcek112
tomcek112

Reputation: 1626

Quite and old question but I had the same issue today. Cleanest solution I found is to use the concept of currying.

The code for that:

someObj.addEventListener('click', some_function(someVar));

var some_function = function(someVar) {
    return function curried_func(e) {
        // do something here
    }
}

By naming the curried function it allows you to call Object.removeEventListener to unregister the eventListener at a later execution time.

Upvotes: 90

eclos
eclos

Reputation: 221

Function.prototype.bind() is the way to bind a target function to a particular scope and optionally define the this object within the target function.

someObj.addEventListener("click", some_function.bind(this), false);

Or to capture some of the lexical scope, for example in a loop:

someObj.addEventListener("click", some_function.bind(this, arg1, arg2), false);

Finally, if the this parameter is not needed within the target function:

someObj.addEventListener("click", some_function.bind(null, arg1, arg2), false);

Upvotes: 21

Suneel Kumar
Suneel Kumar

Reputation: 5831

I was stuck in this as I was using it in a loop for finding elements and adding listner to it. If you're using it in a loop, then this will work perfectly

for (var i = 0; i < states_array.length; i++) {
     var link = document.getElementById('apply_'+states_array[i].state_id);
     link.my_id = i;
     link.addEventListener('click', function(e) {   
        alert(e.target.my_id);        
        some_function(states_array[e.target.my_id].css_url);
     });
}

Upvotes: 2

Nate
Nate

Reputation: 2519

The following approach worked well for me. Modified from here.

function callback(theVar) {
  return function() {
    theVar();
  }
}

function some_other_function() {
  document.body.innerHTML += "made it.";
}

var someVar = some_other_function;
document.getElementById('button').addEventListener('click', callback(someVar));
<!DOCTYPE html>
<html>
  <body>
    <button type="button" id="button">Click Me!</button>
  </body>
</html>

Upvotes: 1

Felipe
Felipe

Reputation: 51

One way is doing this with an outer function:

elem.addEventListener('click', (function(numCopy) {
  return function() {
    alert(numCopy)
  };
})(num));

This method of wrapping an anonymous function in parentheses and calling it right away is called an IIFE (Immediately-Invoked Function Expression)

You can check an example with two parameters in http://codepen.io/froucher/pen/BoWwgz.

catimg.addEventListener('click', (function(c, i){
  return function() {
    c.meows++;
    i.textContent = c.name + '\'s meows are: ' + c.meows;
  }
})(cat, catmeows));

Upvotes: 5

Gennadiy Sherbakha
Gennadiy Sherbakha

Reputation: 11

    var EV = {
        ev: '',
        fn: '',
        elem: '',
        add: function () {
            this.elem.addEventListener(this.ev, this.fn, false);
        }
    };

    function cons() {
        console.log('some what');
    }

    EV.ev = 'click';
    EV.fn = cons;
    EV.elem = document.getElementById('body');
    EV.add();

//If you want to add one more listener for load event then simply add this two lines of code:

    EV.ev = 'load';
    EV.add();

Upvotes: 1

Šerg
Šerg

Reputation: 803

The following code worked fine for me (firefox):

for (var i=0; i<3; i++) {
   element = new ...   // create your element
   element.counter = i;
   element.addEventListener('click', function(e){
        console.log(this.counter);
        ...            // another code with this element
   }, false);
}

Output:

0
1
2

Upvotes: 0

bob
bob

Reputation: 8005

Sending arguments to an eventListener's callback function requires creating an isolated function and passing arguments to that isolated function.

Here's a nice little helper function you can use. Based on "hello world's" example above.)

One thing that is also needed is to maintain a reference to the function so we can remove the listener cleanly.

// Lambda closure chaos.
//
// Send an anonymous function to the listener, but execute it immediately.
// This will cause the arguments are captured, which is useful when running 
// within loops.
//
// The anonymous function returns a closure, that will be executed when 
// the event triggers. And since the arguments were captured, any vars 
// that were sent in will be unique to the function.

function addListenerWithArgs(elem, evt, func, vars){
    var f = function(ff, vv){
            return (function (){
                ff(vv);
            });
    }(func, vars);

    elem.addEventListener(evt, f);

    return f;
}

// Usage:

function doSomething(withThis){
    console.log("withThis", withThis);
}

// Capture the function so we can remove it later.
var storeFunc = addListenerWithArgs(someElem, "click", doSomething, "foo");

// To remove the listener, use the normal routine:
someElem.removeEventListener("click", storeFunc);

Upvotes: 4

Hello World
Hello World

Reputation: 915

Here's yet another way (This one works inside for loops):

var someVar = some_other_function();
someObj.addEventListener("click", 

function(theVar){
    return function(){some_function(theVar)};
}(someVar),

false);

Upvotes: 13

StanE
StanE

Reputation: 2783

There is a special variable inside all functions: arguments. You can pass your parameters as anonymous parameters and access them (by order) through the arguments variable.

Example:

var someVar = some_other_function();
someObj.addEventListener("click", function(someVar){
    some_function(arguments[0]);
}, false);

Upvotes: 2

Sergey Ilinsky
Sergey Ilinsky

Reputation: 31545

There is absolutely nothing wrong with the code you've written. Both some_function and someVar should be accessible, in case they were available in the context where anonymous

function() { some_function(someVar); } 

was created.

Check if the alert gives you the value you've been looking for, be sure it will be accessible in the scope of anonymous function (unless you have more code that operates on the same someVar variable next to the call to addEventListener)

var someVar; 
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);

Upvotes: 302

Related Questions