removeEventListener pada fungsi anonim di JavaScript

102

Saya memiliki objek yang memiliki metode di dalamnya. Metode ini dimasukkan ke dalam objek di dalam fungsi anonim. Ini terlihat seperti ini:

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

(ada lebih banyak kode, tapi ini cukup untuk menunjukkan masalahnya)

Sekarang saya ingin menghentikan pendengar acara dalam beberapa kasus. Oleh karena itu saya mencoba melakukan removeEventListener tetapi saya tidak tahu bagaimana melakukan ini. Saya telah membaca di pertanyaan lain bahwa tidak mungkin memanggil removeEventListener pada fungsi anonim, tetapi apakah ini juga yang terjadi dalam situasi ini?

Saya memiliki metode yang dibuat di dalam fungsi anonim dan oleh karena itu saya pikir itu mungkin. Terlihat seperti ini:

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

Mengapa saya tidak bisa melakukan ini?

Apakah ada cara lain (baik) untuk melakukan ini?

Info bonus; ini hanya harus bekerja di Safari, maka dukungan IE hilang.

bitkid
sumber
Mengapa tidak menyimpan fungsi ini? Penangan kejadian mungkin bukan fungsi anonim.
Kiroid
2
Saya menyadari ini agak terlambat, tetapi Anda juga dapat menggunakan metode Node.setUserData /Node.getUserData untuk menyimpan data tentang suatu elemen. Misalnya, saat Anda perlu menyetel pendengar anon (dan dapat menghapusnya), pertama-tama setel data pengguna ke fungsi anon (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);lalu lakukan Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false); ... dan sama untuk removeEventListener. Semoga Anda bisa melihat ini dengan baik.
Mengejar
EDIT: Sesuai komentar sebelumnya, saya kira itu hanya berfungsi di Firefox ... Saya baru saja mencoba IE8 (IE9 tidak diketahui), Safari 5.1.2, Chrome (?), Opera 11..Tidak ada dadu
Chase
Kemungkinan duplikat JavaScript: hapus pendengar acara
Heretic Monkey

Jawaban:

77

Saya percaya itu adalah inti dari fungsi anonim, tidak memiliki nama atau cara untuk merujuknya.

Jika saya jadi Anda, saya hanya akan membuat fungsi bernama, atau memasukkannya ke dalam variabel sehingga Anda memiliki referensi ke sana.

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

Anda kemudian dapat menghapusnya dengan

window.document.removeEventListener("keydown", handler);   
Adam Heath
sumber
3
Terimakasih atas balasan anda. Saya pergi dengan: var handler; window.document.addEventListener ("keydown", handler = function (e) {Tapi yang saya tidak mengerti adalah mengapa "ini" tidak merujuk pada event listener. Bukankah seharusnya event listener menjadi objek?
bitkid
1
Kata thiskunci bisa membingungkan. Tempat yang baik untuk membacanya adalah quirksmode.org/js/this.html
Adam Heath
Terima kasih banyak. Ini sangat membantu.
bitkid
Saya mencoba melakukan ini untuk memblokir iklan yang sangat gigih di situs web. Saya tahu bahwa ini adalah inti dari fungsi anonim, tetapi itu tidak berarti saya tidak ingin tahu bagaimana melakukannya.
Wyatt8740
@bitkid: Di dalam fungsi handler (dengan asumsi ini bukan fungsi panah), thismengacu pada elemen yang ditambahkan pendengar, bukan kejadian itu sendiri (yang akan menjadi parameter e). Karena itu this === e.currentTarget. baca developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
chharvey
100

jika Anda berada di dalam fungsi sebenarnya, Anda bisa menggunakan arguments.callee sebagai referensi ke fungsi tersebut. seperti dalam:

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

EDIT: Ini tidak akan berfungsi jika Anda bekerja dalam mode ketat ( "use strict";)

Otto Nascarella
sumber
2
Ini bagus karena mempertahankan keuntungan dari fungsi anonim (tidak mencemari namespace dll.).
bompf
3
mencoba ini di aplikasi WinJS, mendapat kesalahan berikutnya: "Mengakses properti 'callee' dari objek argumen tidak diizinkan dalam mode ketat"
Valentin Kantor
1
@ValentinKantor: Itu karena sesuatu di kode ada yang "gunakan ketat"; pernyataan, dan Anda tidak dapat menggunakan callee dalam mode ketat.
OMA
19
Beri nama fungsi sebaris dan Anda dapat mereferensikannya tanpa menggunakan arguments.callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Harry Love
4
Seperti yang dinyatakan di Mozilla: "Peringatan: ECMAScript edisi ke-5 (ES5) melarang penggunaan arguments.callee () dalam mode ketat. Hindari menggunakan arguments.callee () dengan memberi nama ekspresi fungsi atau menggunakan deklarasi fungsi di mana fungsi harus menyebut dirinya sendiri. "
Bung
50

Sebuah versi solusi Otto Nascarella yang bekerja dalam mode ketat adalah:

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});
Melle
sumber
4
Solusi yang bagus!
Eric Norcross
2
Ini mungkin bukan cara yang benar tapi ini yang termudah.
Vignesh
7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

Mungkin ada beberapa fungsi anonim, keydown 1

Peringatan: hanya berfungsi di Chrome Dev Tools& tidak dapat digunakan dalam kode : tautan

Isayevskiy_Sergey
sumber
2
Terima kasih, Anda telah memecahkan teka-teki, setidaknya di Chrome, yang menurut banyak pelawak tidak mungkin. Man, kamu seperti ... Batman!
JasonXA
20
getEventListenerstampaknya menjadi bagian dari Alat Pengembang Chrome, jadi tidak benar-benar dapat digunakan untuk apa pun selain debugging.
DBS
1
Baru saja mencobanya, dikonfirmasi bahwa ini hanya tersedia di Devtools dan tidak saat disertakan dalam skrip di dalam halaman.
Andres Riofrio
5

di peramban modern, Anda dapat melakukan hal berikut ...

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

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

shunryu111
sumber
Tenang sampai Anda mengetahui bahwa tidak ada versi IE atau edge <16 yang benar-benar mendukung fitur ini. Setidaknya dalam 5 tahun kita dapat menggunakan ini sejak IE akan (baca: harus) tidak digunakan lagi, Edge akan menggantikannya dan akan menggunakan mesin webkit, bukan "EdgeHTML" mereka sendiri.
SidOfc
1
dengan polyfill ini untuk entri DOM Level 4 Anda akan baik-baik saja npmjs.com/package/dom4
shunryu111
2

Pilihan yang tidak begitu anonim

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

Sejak menerima umpan balik dari Andy ( benar, tetapi seperti banyak contoh lainnya, saya ingin menunjukkan perluasan kontekstual dari gagasan tersebut ), berikut adalah eksposisi yang tidak terlalu rumit :

<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>

Hal ini memungkinkan struktur fungsi anonim yang efektif, menghindari penggunaan callee yang praktis tidak digunakan lagi , dan memungkinkan penghapusan dengan mudah.

Kebetulan : Penghapusan elemen skrip segera setelah menyetel pendengar adalah trik lucu untuk menyembunyikan kode yang lebih disukai tidak terlalu jelas bagi mata yang mengintip ( akan merusak kejutan ;-)

Jadi metodenya ( lebih sederhana ) adalah:

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );
Fred Gandt
sumber
2
Ini terlalu rumit.
Ben Sinclair
@Andy Saya setuju, tapi mencoba menunjukkan bahwa tidak ada cara untuk menghapus fungsi anonim. Itu harus dalam beberapa cara direferensikan (bahkan callee (buruk, M'Kay) mereferensikan fungsi), dan dengan demikian memberikan contoh hanya satu cara (lain) fungsi dapat direferensikan - dan, bagaimana itu dibangun dari bagian-bagian yang sama-sama dapat disimpan untuk referensi nanti (bagian penting). Jelas sekali, fungsi yang benar-benar anonim dibangun dengan seketika, sehingga mengetahui nanti tindakan / jenis peristiwa mana dan apakah penangkapan digunakan juga harus diketahui. Bagaimanapun, inilah metode yang lebih baik :-)
Fred Gandt
Bekerja dengan sempurna untuk saya. Saya tidak bisa melihat cara lain untuk menyampaikan argumen ke dalam fungsi, karena tidak bisa anonim.
nicodemus13
2

Ini tidak ideal karena menghapus semua, tetapi mungkin berhasil untuk kebutuhan Anda:

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

Kloning node menyalin semua atribut dan nilainya, termasuk pendengar intrinsik (in-line). Itu tidak menyalin event listener yang ditambahkan menggunakan addEventListener ()

Node.cloneNode ()

Komunitas
sumber
Ini benar-benar brilian
Ahmad Alfy
1

JavaScript : metode addEventListener mendaftarkan listener yang ditentukan pada EventTarget (Element | document | Window) tempat pemanggilannya.

EventTarget. addEventListener ( event_type , handler_function, Bubbling | Capturing );

Mouse, uji Contoh acara Keyboard di 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);

Metode removeEventListener menghapus event listener yang sebelumnya terdaftar dengan EventTarget.addEventListener ().

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

caniuse

Yash
sumber
1

Untuk memberikan pendekatan yang lebih mutakhir untuk ini:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);
Jonatas Walker
sumber
2
Penjelasannya akan bagus.
Poul Bak
1

Saya telah menemukan masalah yang sama dan ini adalah solusi terbaik yang bisa saya dapatkan:

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

Harap diingat bahwa saya hanya menguji ini untuk windowelemen dan 'mousemove'acara, jadi mungkin ada beberapa masalah dengan pendekatan ini.

Gark Garcia
sumber
0

Mungkin bukan solusi terbaik dalam hal apa yang Anda minta. Saya masih belum menentukan metode yang efisien untuk menghapus fungsi anonim yang dideklarasikan sejalan dengan pemanggilan event listener.

Saya pribadi menggunakan variabel untuk menyimpan <target>dan mendeklarasikan fungsi di luar pemanggilan event listener misalnya:

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

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

Kemudian untuk menghapus pendengar:

target.removeEventListener('click', myFunc);

Bukan rekomendasi teratas yang akan Anda terima tetapi untuk menghapus fungsi anonim, satu-satunya solusi yang menurut saya berguna adalah menghapus lalu mengganti elemen HTML. Saya yakin pasti ada metode JS vanilla yang lebih baik tetapi saya belum melihatnya.

Shay Connolly
sumber
0

Saya tahu ini adalah utas yang cukup lama, tetapi saya pikir saya mungkin akan memasukkan dua sen saya untuk mereka yang merasa berguna.

Skrip (maaf tentang nama metode yang tidak kreatif):

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

Dan Anda bisa menggunakannya seperti ini:

// 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);
GROVER.
sumber
-4
window.document.onkeydown = function(){};
Dun
sumber
Why not = undefined? Badass nyata.
Ben Sinclair
2
Ini tidak akan menghapus penangan apa pun yang telah didaftarkan addEventListener.
Luc125