addEventListenerリスナー関数に引数を渡す方法は?


304

状況はやや似ています-

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

問題は、の値がsomeVarのリスナー関数内に表示されないことaddEventListenerです。この場合、おそらく新しい変数として扱われます。


8
問題に関する非常に明確な記事:toddmotto.com/avoiding-anonymous-javascript-functions
のび太

最もクリーンな方法ではありませんが、仕事をします。someVarが数字またはテキストのみの場合は、eval( 'someObj.addEventListener( "click"、function(){some_function(' + someVar + ');});');
Ignas2526 2014年

ただ、今日はこの問題を持っていた-ここで指定したソリューションは、(他のソリューション等、ループ問題のような問題を持っている)正しい- stackoverflow.com/a/54731362/984471
ManoharさんレディPoreddy

回答:


207

あなたが書いたコードには何の問題もありません。両方ともsome_functionsomeVar匿名のコンテキストで利用できる場合は、アクセス可能でなければなりません。

function() { some_function(someVar); } 

作成されました。

アラートがあなたが探していた値を提供しているかどうかを確認し、匿名関数のスコープ内でアクセスできることを確認してください(someVarへの呼び出しの横にある同じ変数を操作するコードが他にない限りaddEventListener

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

86
これはforループでは機能しません。私は常に最新の値を取得し、その反復に属する値は取得しません。解決策はありますか?
iMatoria

6
なぜそれがループで機能しないのか誰か知っていますか?その振る舞いの理由は何ですか?
モルフィドン

3
@Morfidonは、グローバル変数を持つ関数がJavaScriptのクロージャとして機能するため、レキシカルスコープ外の環境を記憶するためです。同じ環境で異なる関数を作成するだけの場合、それらは同じ環境を参照します。
bugwheels94

16
@Morfidon:ループでは、someVarの値はリスナーが追加されたときの値ではなく、リスナーが実行されたときの値です。リスナーが実行されると、ループはすでに終了しているため、someVarの値はループが終了したときの値になります。
www.admiraalit.nl 2015年

4
私はちょうど作成することを発見した@iMatoria bound function使用.bind()方法は、ループの問題を解決しますdeveloper.mozilla.org/en/docs/Web/JavaScript/Reference/...
ルークTオブライエン

353

イベントのターゲット属性から引数を取得しないのはなぜですか?

例:

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はプロトタイプ指向の言語です。覚えておいてください。


16
これは、 'removeEventListener'関数の後に使用できるので正しい答えです。
user5260143

14
そうじゃないのevt.currentTarget.myParam?「someInput」内に別の要素がある場合はevt.target、内部要素を参照している可能性があります。(jsfiddle.net/qp5zguay/1
Herbertusz

これは保存されthisます!typescriptでこのメソッドを使用するには、要素であるanyか、サブタイプにする必要があります。
Old Badman Grey

1
私の変数は未定義として戻ってきます...それを修正する方法についての考えはありますか?
nomaam 2018

1
場合addEventListenerのためにあるdocumentevt.target.myParam私のために動作しませんでした。evt.currentTarget.myParam代わりに使用しなければなりませんでした。
turrican_34

67

この質問は古いですが、後世のために、ES5の.bind()を使用して別の方法を提供すると思いました。:)

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

bindに渡す引数(他の関数)として最初のパラメーターを使用してリスナー関数を設定する必要があり、2番目のパラメーターが(最初のパラメーターではなく)イベントになっていることに注意してください。 。


1
Function.prototype.bind()は、この問題を解決する最良の方法です。さらに、ループ内で直感的に機能し、必要なレキシカルスコープを取得します。匿名関数、IIFE、またはオブジェクトに追加された特別なプロパティはありません。
Clint Pachl

IIFEとbind()の長所と短所を参照してください。
Clint Pachl

1
イベントリスナーを削除するFunction.prototype.bind()ことはできません。代わりにカリー化関数を使用することをお勧めします(@ tomcek112の回答を参照)
pldg

注:some_other_funcは変数であり、任意の値を渡すことができますsome_func
hitautodestruct

28

'bind'を使用して、必要なすべての引数をバインドできます。

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

このように、あなたは常に得るでしょうeventarg1に渡され、他のものをmyPrettyHandler

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


ありがとう!すでに試しました.bind()が、最初のパラメータとしてnullがありませんでした。うまくいきませんでした。
Larphoid

必要はありませんnull、それはして罰金を動作しますが、.bind(event, arg1)少なくともVueJSで、。
DevonDahon

27

かなり古い質問ですが、今日も同じ問題がありました。私が見つけた最もクリーンな解決策は、カレーの概念を使用することです

そのためのコード:

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

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

カリー化された関数に名前を付けることにより、Object.removeEventListenerを呼び出して、後で実行するときにeventListenerの登録を解除できます。


4
カレー機能について言及しているこの答えに出会えてうれしい。どのようにしてイベントリスナーを削除しますか?
ボブは

3
良い用語を見るのは素晴らしい。カリー化された関数に名前を付けることにより、イベントリスナーを削除できるはずです。編集を提案します。
マシューブレント

some_function(var)は新しく作成された関数を毎回返すため、この回答はaddEventListenerが呼び出されるたびに関数を登録します。
Yahia

私はリスナーcuzを削除するためにカリー化された関数に名前を付ける必要があるという考えが好きではありません。それから、追跡しなければならない2つのdiff名前空間を扱います
oldboy

19

関数を変数として宣言することにより、引数を持つイベントリスナーを追加および削除できます。

myaudio.addEventListener('ended',funcName=function(){newSrc(myaudio)},false);

newSrcパラメータfuncNameが関数名変数であるmyaudioを使用するメソッド

あなたはリスナーを削除することができます myaudio.removeEventListener('ended',func,false);


12

クロージャーと呼ばれるjavascript機能を介して、(参照ではなく)値によってsomevarを渡すことができます。

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

または、次のような一般的なラップ関数を記述できます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'

これwrapEventCallback(func,var1,var2)は次のようなものです:

func.bind(null, var1,var2)

1
この回答をどうもありがとう!OPはこれを探していませんでしたが、「argsをaddEventListenerに渡す方法」をgoogleに入力した人はあなたの答えを探していると思います。それはもう少し説明が必要です:)私はそれを編集しています。
シンダルス

9

someVar値はsome_function()、リスナーからではなく、コンテキストでのみアクセス可能である必要があります。リスナー内で使用したい場合は、次のようにする必要があります。

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

newVar代わりに使用します。

もう1つの方法は、リスナーで(新しいローカル変数として)使用するためにsomeVarから値を返すことですsome_function()

var someVar = some_function(someVar);

9

これはさらに別の方法です(これはforループ内で機能します)。

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

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

false);

2
これが最良の方法です。醜いですが、無名関数に引数を送ることで変数を取得するため、ループ内で効果的です。
ボブ2015

9

Function.prototype.bind()は、ターゲット関数を特定のスコープにバインドし、オプションでthisターゲット関数内のオブジェクトを定義する方法です。

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

または、たとえばループで、字句スコープの一部をキャプチャするには、次のようにします。

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

最後に、thisターゲット関数内でパラメーターが必要ない場合:

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

7

使用する

   el.addEventListener('click',
    function(){
        // this will give you the id value 
        alert(this.id);    
    },
false);

そして、この無名関数にカスタム値を渡したい場合、それを行う最も簡単な方法は

 // this will dynamically create property a property
 // you can create anything like el.<your  variable>
 el.myvalue = "hello world";
 el.addEventListener('click',
    function(){
        //this will show you the myvalue 
        alert(el.myvalue);
        // this will give you the id value 
        alert(this.id);    
    },
false);

私のプロジェクトでは完全に機能します。これが役に立てば幸い


はい、それはforループ内で期待されるスコープを維持するので、間違いなく役立ちました。
j4v1

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

これは、イベントを適切に渡す方法です。


すばらしい、これは私がほとんど結論付けていた方法です-バインドの中にない余分なイベントが誤って渡された場合(angularのように)、この場合は自動的に発生します。
Manohar Reddy Poreddy

3

eventListenerのコールバック関数に引数を送信するには、独立した関数を作成し、その独立した関数に引数を渡す必要があります。

ここにあなたが使える素晴らしい小さなヘルパー関数があります。上記の「hello world's」の例に基づいています。)

また、リスナーをきれいに削除できるように、関数への参照を維持することも必要です。

// 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);

この回答は'15からのものですが、useRefフックを使用してこの問題を処理するために私がまさに必要とするものです。refフックを使用していて、コンポーネントのアンマウント時にクリーンアップできるリスナーが必要な場合は、これで完了です。の4番目の引数storeFuncは、参照変数にする必要があります。このようuseEffectであなたのリスナーの除去を入れて、あなたがしている良いが行くように:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
ロブ・B

3

1つの方法は、これを外部関数で行うことです。

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

匿名関数を括弧で囲んですぐに呼び出すこの方法は、IIFE(Immediately-Invoked Function Expression)と呼ばれます。

http://codepen.io/froucher/pen/BoWwgzの 2つのパラメーターを使用して例を確認できます

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

3

関数の呼び出しを誤って使用していない場合は、bind実際にはbindメソッドから返される新しい関数が作成されます。これは後で問題が発生するか、基本的には匿名関数のように、イベントリスナーを削除する場合に発生します。

// 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 */);

それを覚えておいてください。

ES6を使用している場合は、提案されたものと同じことを行うことができますが、少しすっきりしています。

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

3

素敵な1行の代替

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

 //some action...

}

2

また、これらを試してください(IE8 +Chrome。FFについては知りません):

function addEvent(obj, type, fn) {
    eval('obj.on'+type+'=fn');
}

function removeEvent(obj, type) {
    eval('obj.on'+type+'=null');
}

// Use :

function someFunction (someArg) {alert(someArg);}

var object=document.getElementById('somObject_id') ;
var someArg="Hi there !";
var func=function(){someFunction (someArg)};

// mouseover is inactive
addEvent (object, 'mouseover', func);
// mouseover is now active
addEvent (object, 'mouseover');
// mouseover is inactive

タイプミスがないことを願っています:-)


完全な答えを出すのはどれほど難しいでしょうか?これをFFでテストする必要がありますか?まあ、私は気にしない...
StefanNch 2013年

2

要素を見つけてそこにリスナーを追加するためにループでそれを使用していたので、私はこれに行き詰まりました。ループで使用している場合、これは完全に機能します

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);
     });
}

2

2019では、APIが大幅に変更され、バグを修正せずにベストアンサーが機能しなくなりました。

いくつかの作業コードを共有します。

上記のすべての回答に触発されました。

 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;)
   }

1

すべての関数の内部に特別な変数があります:引数。パラメーターを匿名パラメーターとして渡し、arguments変数を介して(順序で)アクセスできます。

例:

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

うーん...反対票の理由は何ですか?それがあなたが探しているものではなかった場合は、あなたが何を意味しているのかをより明確に説明してください(質問はすでに回答済みです)。しかし、私のコードはあなたが求めたものに答えていませんか?特殊変数「引数」を使用すると、関数内のすべてのパラメーターにアクセスできます。
StanE 2015年

1
    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();

1

次のアプローチは私にはうまくいきました。ここから変更。

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>


0

次の答えは正しいですが、yuicompressorを使用してjsファイルを圧縮した場合、以下のコードはIE8で機能しません。(実際、IE8を使用している米国のほとんどの人々)

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

したがって、上記の問題を次のように修正でき、すべてのブラウザで正常に機能します

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

実稼働環境でjsファイルを圧縮している人にとって有用であることを願っています。

幸運を!!


0

次のコードは私(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);
}

出力:

0
1
2

これは一体何なのでしょう?
NiCkニューマン2015


0

おそらく最適ではありませんが、スーパーjsに精通していない人には十分シンプルです。addEventListenerを呼び出す関数を独自の関数に配置します。このようにして、渡された関数値は独自のスコープを維持し、その関数を好きなだけ反復することができます。

例画像とファイル名のプレビューをキャプチャしてレンダリングする必要があるため、ファイルの読み取りを行いました。複数ファイルのアップロードタイプを使用する場合、非同期の問題を回避するのにしばらく時間がかかりました。異なるファイルをアップロードしているにもかかわらず、すべてのレンダリングで誤って同じ「名前」が表示されました。

当初、すべてのreadFile()関数はreadFiles()関数内にありました。これにより、非同期スコープの問題が発生しました。

    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

0

追加したいだけです。イベントリスナーのチェックボックスを更新する関数を誰かが追加している場合は、チェックボックスを更新するevent.target代わりにthisを使用する必要があります。


0

私は非常に単純なアプローチをとっています。それは私を助けたので、これは他の人のために働くかもしれません。それは...複数の要素/変数に同じ関数を割り当てており、参照を渡したい場合、最も簡単な解決策は...

function Name()
{

this.methodName = "Value"

}

それでおしまい。それは私のために働いた。 とても簡単。


-1

バインドの使用ほどエレガントではないかもしれませんが、ループ内のイベントに対しては有効です。

for (var key in catalog){
    document.getElementById(key).my_id = key
    document.getElementById(key).addEventListener('click', function(e) {
        editorContent.loadCatalogEntry(e.srcElement.my_id)
    }, false);
}

それはグーグルクロム拡張のためにテストされました、そして多分e.srcElementは他のブラウザでe.sourceによって置き換えられなければなりません

Imatoriaによって投稿されたコメントを使用してこの解決策を見つけましたが、十分な評判がないため、有用とマークできません:D


-1

このソリューションは見栄えが良いかもしれません

var some_other_function = someVar => function() {
}

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

またはバインド変数も良いでしょう

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.