JavaScriptの無名関数に対するremoveEventListener


101

その中にメソッドを持つオブジェクトがあります。これらのメソッドは、無名関数内のオブジェクトに配置されます。次のようになります。

var t = {};
window.document.addEventListener("keydown", function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
});  

(もっと多くのコードがありますが、これは問題を示すのに十分です)

次に、イベントリスナーを停止したい場合があります。したがって、私はremoveEventListenerを実行しようとしていますが、これを行う方法を理解できません。他の質問で、匿名関数でremoveEventListenerを呼び出すことは不可能であると読みましたが、これはこの状況の場合にも当てはまりますか?

無名関数内に作成されたメソッドがあり、それが可能だと思いました。このように見えます:

t.disable = function() {
    window.document.removeEventListener("keydown", this, false);
}

なぜこれができないのですか?

これを行う他の(良い)方法はありますか?

ボーナス情報; これはSafariでのみ機能する必要があるため、IEサポートがありません。


この関数を保存しないのはなぜですか?イベントハンドラーは匿名関数ではない可能性があります。
キリルイド

2
これは少し遅れていることを理解していますが、Node.setUserData /Node.getUserDataメソッドを使用して要素に関するデータを保存することもできます。たとえば、anonリスナーを設定する必要がある場合(およびそれを削除できる場合)、最初に(Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);userdataをanon関数に設定し、次にElem.addEventListener( 'event'、Elem.getUserData( 'eventListener')、false);を実行します。...およびremoveEventListenerについても同様です。よろしくお願いします。
追跡

編集:以前のコメントのとおり、Firefoxでしか機能しないと思います... IE8(IE9は不明)、Safari 5.1.2、Chrome(?)、Opera 11 ..サイコロなし
チェイス

回答:


76

それが無名関数の要点だと思います。名前や参照方法がありません。

私があなただったら、名前付きの関数を作成するか、それを変数に入れて参照できるようにします。

var t = {};
var handler = function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
};
window.document.addEventListener("keydown", handler);

その後、それを削除することができます

window.document.removeEventListener("keydown", handler);   

3
お返事ありがとうございます。私は一緒に行きました:var handler; window.document.addEventListener( "keydown"、handler = function(e){しかし、 "this"がイベントリスナーを参照しない理由がわかりません。イベントリスナーはオブジェクトではありませんか?
bitkid

1
thisキーワードが混乱することができます。それを読むのに適した場所はquirksmode.org/js/this.htmlです
Adam Heath

どうもありがとうございました。これは最も役に立ちました。
ビットキッド

私はこれを実行して、Webサイト上の非常に永続的な広告をブロックしようとしています。これが無名関数の要点であることは知っていますが、だからといってその方法を知りたくないという意味ではありません。
Wyatt8740

@bitkid:ハンドラー関数内で(矢印関数ではないと想定)、はthis、リスナー自体が追加される要素を参照しますe。イベント自体(パラメーターになる)ではありません。したがってthis === e.currentTargetdeveloper.mozilla.org/en-US/docs/Web/API/EventTarget/…をお
chharvey

99

実際の関数の内部にいる場合は、arguments.calleeを関数への参照として使用できます。のように:

button.addEventListener('click', function() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', arguments.callee);
});

編集: これは、厳密モード("use strict";)で作業している場合は機能しません


1
匿名関数(名前空間を汚染しないなど)の利点を維持するため、これは素晴らしいことです。
bompf 2012

3
WinJSアプリでこれを試して、次のエラーが発生しました:「引数オブジェクトの「callee」プロパティへのアクセスは、厳密モードでは許可されていません」
Valentin Kantor

1
@ValentinKantor:コードに何か「厳密な使用」があるためです。ステートメント、および厳格モードでは呼び出し先を使用できません。
OMA 2013年

19
インライン関数に名前を付けて、あなたは、arguments.calleeのに頼ることなく、それを参照することができますbutton.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
ハリー・ラブ

4
Mozillaで述べられているように:「警告:ECMAScript(ES5)の第5版は、strictモードでのarguments.callee()の使用を禁止しています。自分自身を呼び出す必要があります。」
2016

50

strictモードで機能するOtto Nascarellaのソリューションのバージョンは次のとおりです。

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});

4
美しいソリューション!
エリックノークロス2017年

2
これは正しい方法ではないかもしれませんが、これが最も簡単な方法です。
Vignesh、

7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

いくつかの匿名関数である可能性があります、キーダウン1

警告:でのみ機能しChrome Dev Tools、コードでは使用できませんリンク


2
ありがとう、多くのジョーカーが不可能だと言っていたので、少なくともChromeでなぞなぞを解決しました。男、あなたは…バットマンみたい!
JasonXA

20
getEventListenersはChrome Dev-toolsの一部のようですので、デバッグ以外には何も使えません。
DBS 2016

1
試してみたところ、Devtoolsでのみ利用可能であり、ページ内のスクリプトに含まれている場合は利用できないことが確認されました。
Andres Riofrio 2018年

5

最新のブラウザでは、次のことができます...

button.addEventListener( 'click', () => {
    alert( 'only once!' );
}, { once: true } );

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters


IEのバージョンも16未満のエッジも実際にこの機能をサポートしていないことがわかるまでは、クールです。少なくとも5年間はこれを使用できます。それ以降、IEは非推奨になり(推奨:推奨)、Edgeが代わりになり、独自の "EdgeHTML"の代わりにwebkitエンジンを使用します。
SidOfc 2018

1
このDOM Level 4エントリのポリフィルを使用すると、npmjs.com / package / dom4
shunryu111が

2

それほど匿名ではないオプション

element.funky = function() {
    console.log("Click!");
};
element.funky.type = "click";
element.funky.capt = false;
element.addEventListener(element.funky.type, element.funky, element.funky.capt);
// blah blah blah
element.removeEventListener(element.funky.type, element.funky, element.funky.capt);

アンディからフィードバック受け取ったのでかなり正しいですが、多くの例と同様に、私はアイデアのコンテキスト展開を示したいと思っていました)、以下それほど複雑でない説明です。

<script id="konami" type="text/javascript" async>
    var konami = {
        ptrn: "38,38,40,40,37,39,37,39,66,65",
        kl: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
    };
    document.body.addEventListener( "keyup", function knm ( evt ) {
        konami.kl = konami.kl.slice( -9 );
        konami.kl.push( evt.keyCode );
        if ( konami.ptrn === konami.kl.join() ) {
            evt.target.removeEventListener( "keyup", knm, false );

            /* Although at this point we wish to remove a listener
               we could easily have had multiple "keyup" listeners
               each triggering different functions, so we MUST
               say which function we no longer wish to trigger
               rather than which listener we wish to remove.

               Normal scoping will apply to where we can mention this function
               and thus, where we can remove the listener set to trigger it. */

            document.body.classList.add( "konami" );
        }
    }, false );
    document.body.removeChild( document.getElementById( "konami" ) );
</script>

これにより、実質的に匿名の関数構造が可能になり、実質的に非推奨のcalleeの使用が回避され、簡単に削除できます。

ちなみに、リスナーを設定した直後のスクリプト要素の削除は、コードを非表示にするためのかわいいトリックであり、こじ開ける目には明白ではありませんでした(驚きを台無しにするでしょう)。 ;-)

したがって、メソッド(より単純に)は次のとおりです。

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );

2
これは複雑すぎる。
ベンシンクレア

@Andy私もそう思いますが、匿名関数を削除する方法がないことを示しようとしていました。しなければならないということ、それが部分で組み込まれているか、と-いくつかの方法で参照することで(でも呼び出し先が(、M'Kay悪い)関数を参照している)ので、関数が参照することができるただ一つ(他の)道の例を提供します後で参照するために同様に保存できます(重要な部分)。明らかに、匿名関数はいくぶんその場で構築されているので、後でどのイベントアクション/タイプ、およびキャプチャが使用されたかどうかも知る必要があります。とにかく、これはより良い方法です:-)
フレッドガント

私には完璧に働きました。関数に引数を渡す別の方法は、匿名ではないのでわかりませんでした。
nicodemus13 2014年

2

これはすべてを削除するため、理想的ではありませんが、ニーズに応じて機能する可能性があります。

z = document.querySelector('video');
z.parentNode.replaceChild(z.cloneNode(1), z);

ノードのクローンを作成すると、組み込み(インライン)リスナーを含め、ノードのすべての属性とその値がコピーされます。addEventListener()を使用して追加されたイベントリスナーはコピーしません

Node.cloneNode()


これは絶対にすばらしい
Ahmad Alfy

1

JavaScript:addEventListener メソッドは、呼び出されたEventTarget(Element | document | Window)に指定されたリスナーを登録します。

EventTarget。addEventListenerevent_type、handler_function、Bubbling | Capturing);

マウス、キーボードイベント WebConsoleでのテスト例:

var keyboard = function(e) {
    console.log('Key_Down Code : ' + e.keyCode);
};
var mouseSimple = function(e) {
    var element = e.srcElement || e.target;
    var tagName = element.tagName || element.relatedTarget;
    console.log('Mouse Over TagName : ' + tagName);    
};
var  mouseComplex = function(e) {
    console.log('Mouse Click Code : ' + e.button);
} 

window.document.addEventListener('keydown',   keyboard,      false);
window.document.addEventListener('mouseover', mouseSimple,   false);
window.document.addEventListener('click',     mouseComplex,  false);

removeEventListener メソッドは、以前にEventTarget.addEventListener()で登録されたイベントリスナーを削除します。

window.document.removeEventListener('keydown',   keyboard,     false);
window.document.removeEventListener('mouseover', mouseSimple,  false);
window.document.removeEventListener('click',     mouseComplex, false);

使ってもいいですか


1

これに最新のアプローチを与えるには:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);

2
説明がいいでしょう。
Poul Bak

1

私は同じ問題に出くわしました、そしてこれは私が得ることができる最良の解決策でした:

/*Adding the event listener (the 'mousemove' event, in this specific case)*/
element.onmousemove = function(event) {
    /*do your stuff*/
};
/*Removing the event listener*/
element.onmousemove = null;

window要素と'mousemove'イベントに対してのみテストしたので、このアプローチに問題がある可能性があることを覚えておいてください。


0

おそらく、あなたが何を求めているかという点では、最善の解決策ではありません。イベントリスナーの呼び出しでインラインで宣言された匿名関数を削除するための効率的な方法はまだ決定していません。

個人的に変数を使用してを格納し<target>、イベントリスナー呼び出しの外部で関数を宣言します。例:

const target = document.querySelector('<identifier>');

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

次に、リスナーを削除します。

target.removeEventListener('click', myFunc);

受け取る最高の推奨事項ではありませんが、匿名関数を削除するには、HTML要素を削除して置き換えることが唯一の解決策です。より良いバニラJSメソッドがあるに違いないと私は確信していますが、私はまだそれを見ていません。


0

私はこれがかなり古いスレッドであることを知っていますが、便利だと思う人のために私の2セントを投入するかもしれないと思いました。

スクリプト(非創造的なメソッド名についての謝罪):

window.Listener = {
    _Active: [],
    remove: function(attached, on, callback, capture){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            if(current[0] === attached && current[1] === on && current[2] === callback){
                attached.removeEventListener(on, callback, (capture || false));
                return this._Active.splice(i, 1);
            }
        }
    }, removeAtIndex(i){
        if(this._Active[i]){
            var remove = this._Active[i];
            var attached = remove[0], on = remove[1], callback = remove[2];
            attached.removeEventListener(on, callback, false);
            return this._Active.splice(i, 1);
        }
    }, purge: function(){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            current[0].removeEventListener(current[1], current[2]);
            this._Active.splice(i, 1);
        }
    }, declare: function(attached, on, callback, capture){
        attached.addEventListener(on, callback, (capture || false));
        if(this._Active.push([attached, on, callback])){
            return this._Active.length - 1;
        }
    }
};

そして、あなたはそれを次のように使うことができます:

// declare a new onclick listener attached to the document
var clickListener = Listener.declare(document, "click" function(e){
    // on click, remove the listener and log the clicked element
    console.log(e.target);
    Listener.removeAtIndex(clickListener);
});

// completely remove all active listeners 
// (at least, ones declared via the Listener object)
Listener.purge();

// works exactly like removeEventListener
Listener.remove(element, on, callback);

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