Javascript / DOM: Bagaimana cara menghapus semua event dari objek DOM?

97

Hanya pertanyaan: Apakah ada cara untuk menghapus semua kejadian dari suatu objek, misalnya div?

EDIT: Saya menambahkan per div.addEventListener('click',eventReturner(),false);acara.

function eventReturner() {
    return function() {
        dosomething();
    };
}

EDIT2: Saya menemukan cara, yang berfungsi, tetapi tidak mungkin digunakan untuk kasus saya:

var returnedFunction;
function addit() {
    var div = document.getElementById('div');
    returnedFunction = eventReturner();
    div.addEventListener('click',returnedFunction,false); //You HAVE to take here a var and not the direct call to eventReturner(), because the function address must be the same, and it would change, if the function was called again.
}
function removeit() {
    var div = document.getElementById('div');
    div.removeEventListener('click',returnedFunction,false);
}
Florian Müller
sumber
Bagaimana Anda melampirkan acara?
Felix Kling
2
Judul menanyakan tentang elemen objek, sedangkan pertanyaan sebenarnya menanyakan tentang peristiwa . Apakah Anda ingin menghapus elemen anak atau acara?
Pär Wieslander
oh d * mn, itu karena saya telah memikirkan hal lain ketika saya menulis bahwa ... saya peduli tentang kejadian
Florian Müller
Saya tidak tahu kasus pastinya, tetapi dalam beberapa kasus sebagai solusi, Anda dapat menggunakan metode 'on *' (seperti div.onclick = function), yang selalu berfungsi dengan satu listener dan mudah dihapus sebagai div.onclick=null. Tentu saja Anda tidak boleh menggunakan addEventListenerdalam kasus ini sama sekali karena itu akan menambahkan pendengar terpisah yang berbeda dari yang ada di onclick.
venimus

Jawaban:

106

Saya tidak yakin apa yang Anda maksud dengan menghapus semua acara . Hapus semua penangan untuk jenis acara tertentu atau semua penangan acara untuk satu jenis?

Hapus semua penangan acara

Jika Anda ingin menghapus semua penangan kejadian (jenis apa pun), Anda dapat menggandakan elemen dan menggantinya dengan klonnya:

var clone = element.cloneNode(true);

Catatan: Ini akan mempertahankan atribut dan turunannya, tetapi tidak akan mempertahankan perubahan apa pun pada properti DOM.


Hapus penangan peristiwa "anonim" dari jenis tertentu

Cara lain adalah dengan menggunakan removeEventListener()tetapi saya rasa Anda sudah mencoba ini dan tidak berhasil. Ini tangkapannya :

Memanggil addEventListenerke fungsi anonim membuat pendengar baru setiap saat. Memanggil removeEventListenerke fungsi anonim tidak berpengaruh . Fungsi anonim membuat objek unik setiap kali dipanggil, ini bukan referensi ke objek yang ada meskipun mungkin memanggilnya. Saat menambahkan event listener dengan cara ini pastikan hanya ditambahkan sekali, bersifat permanen (tidak dapat dihapus) hingga objek yang ditambahkan, dimusnahkan.

Anda pada dasarnya meneruskan fungsi anonim ke addEventListenersebagai eventReturnermengembalikan fungsi.

Anda memiliki dua kemungkinan untuk menyelesaikan ini:

  1. Jangan gunakan fungsi yang mengembalikan fungsi. Gunakan fungsinya secara langsung:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Buat pembungkus untuk addEventListeneritu menyimpan referensi ke fungsi yang dikembalikan dan membuat beberapa removeAllEventsfungsi aneh :

    var _eventHandlers = {}; // somewhere global
    
    const addListener = (node, event, handler, capture = false) => {
      if (!(event in _eventHandlers)) {
        _eventHandlers[event] = []
      }
      // here we track the events and their nodes (note that we cannot
      // use node as Object keys, as they'd get coerced into a string
      _eventHandlers[event].push({ node: node, handler: handler, capture: capture })
      node.addEventListener(event, handler, capture)
    }
    
    const removeAllListeners = (targetNode, event) => {
      // remove listeners from the matching nodes
      _eventHandlers[event]
        .filter(({ node }) => node === targetNode)
        .forEach(({ node, handler, capture }) => node.removeEventListener(event, handler, capture))
    
      // update _eventHandlers global
      _eventHandlers[event] = _eventHandlers[event].filter(
        ({ node }) => node !== targetNode,
      )
    }
    

    Dan kemudian Anda bisa menggunakannya dengan:

    addListener(div, 'click', eventReturner(), false)
    // and later
    removeAllListeners(div, 'click')
    

DEMO

Catatan: Jika kode Anda berjalan untuk waktu yang lama dan Anda membuat serta menghapus banyak elemen, Anda harus memastikan untuk menghapus elemen yang ada di dalamnya _eventHandlerssaat Anda menghancurkannya.

Felix Kling
sumber
@Florian: Tentu saja, kodenya dapat ditingkatkan, tetapi itu akan memberi Anda ide ... selamat membuat kode!
Felix Kling
2
Sudahkah Anda mencoba ini dengan lebih dari satu elemen div? var node sebenarnya akan diubah menjadi string, misalnya '[object HTMLDivElement]' yang berarti Anda akhirnya menambahkan semuanya ke node yang sama.
cstruter
@struter: Benar ... ini adalah masa-masa awal saya menggunakan JavaScript ... Saya akan mengoreksi kode ketika saya menemukan waktu lagi. Terima kasih telah memberi tahu saya.
Felix Kling
Saya kira ada kesalahan ketik, addListener harusnya addEvent
AGamePlayer
Perhatikan bahwa element.parentNodebisa jadi nol. Mungkin harus memberi tanda jika di sekitar potongan kode di atas.
thecoolmacdude
43

Ini akan menghapus semua pendengar dari anak-anak tetapi akan lambat untuk halaman besar. Sangat sederhana untuk ditulis.

element.outerHTML = element.outerHTML;
pabombs
sumber
2
Jawaban yang bagus. Untuk simpul daun, ini tampaknya menjadi ide terbaik.
pengguna3055938
3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLbekerja untuk saya.
Ryan
2
@ Ryan alih-alih document.querySelector('body').outerHTMLAnda cukup mengetikdocument.body.outerHTML
Jay Dadhania
28

Gunakan fungsi event listener sendiri remove(). Sebagai contoh:

getEventListeners().click.forEach((e)=>{e.remove()})
JuanGG
sumber
2
Tidak yakin mengapa ini bukan jawaban yang benar. Pertanyaan muncul berkali-kali di SO dan mungkin jawaban menangani masalah menggunakan metode klon.
jimasun
42
Tampaknya itu getEventListenershanya tersedia di alat pengembang WebKit dan di FireBug. Bukan sesuatu yang bisa Anda sebut dalam kode Anda.
corwin.amber
1
mengalami kesulitan membuat ini bekerja, Anda mengirimkan objek sebagai argumen getEventListeners(). contoh: getEventListeners(document)ataugetEventListeners(document.querySelector('.someclass'))
Luke
10
Ini sepertinya hanya berfungsi di konsol chrome . Bahkan pada skrip Anda di halaman itu tidak berfungsi.
Reuel Ribeiro
4
getEventListeners(document).click.forEach((e)=>{e.remove()})menghasilkan kesalahan ini:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan
11

Seperti yang dikatakan corwin.amber, ada perbedaan antara Webkit dan yang lain.

Di Chrome:

getEventListeners(document);

Yang memberi Anda Objek dengan semua event listener yang ada:

Object 
 click: Array[1]
 closePopups: Array[1]
 keyup: Array[1]
 mouseout: Array[1]
 mouseover: Array[1]
 ...

Dari sini Anda dapat menjangkau pendengar yang ingin Anda hapus:

getEventListeners(document).copy[0].remove();

Jadi Semua pendengar acara:

for(var eventType in getEventListeners(document)) {
   getEventListeners(document)[eventType].forEach(
      function(o) { o.remove(); }
   ) 
}

Di Firefox

Sedikit berbeda karena menggunakan pembungkus pendengar yang tidak berisi fungsi hapus. Anda harus mendapatkan pendengar yang ingin Anda hapus:

document.removeEventListener("copy", getEventListeners(document).copy[0].listener)

Semua pendengar acara:

for(var eventType in getEventListeners(document)) {
  getEventListeners(document)[eventType].forEach(
    function(o) { document.removeEventListener(eventType, o.listener) }
  ) 
}

Saya tersandung dengan posting ini mencoba untuk menonaktifkan perlindungan salinan situs berita yang mengganggu.

Nikmati!

Jmakuc
sumber
Ini adalah jawaban terbaik untuk menargetkan penghapusan pendengar, seperti saat menghapus pendengar tertentu pada node dokumen tanpa menghapus semua pendengarnya.
Trevor
Ini bekerja dengan baik dan jawaban terbaik. @JakTerimakasih banyak.
Thavamani Kasi
5
Firefox memberi tahu saya bahwa getEventListenersitu tidak ditentukan?
rovyko
Ini adalah solusi yang buruk karena saat ini getEventListeners hanya didukung oleh Chrome.
Michael Paccione
1

Anda bisa menambahkan fungsi hook untuk mencegat semua panggilan ke addEventHandler. Pengait akan mendorong pawang ke daftar yang dapat digunakan untuk pembersihan. Sebagai contoh,

if (EventTarget.prototype.original_addEventListener == null) {
    EventTarget.prototype.original_addEventListener = EventTarget.prototype.addEventListener;

    function addEventListener_hook(typ, fn, opt) {
        console.log('--- add event listener',this.nodeName,typ);
        this.all_handlers = this.all_handlers || [];
        this.all_handlers.push({typ,fn,opt});
        this.original_addEventListener(typ, fn, opt);  
    }

    EventTarget.prototype.addEventListener = addEventListener_hook;
}

Anda harus memasukkan kode ini di dekat bagian atas halaman web utama Anda (misalnya index.html). Selama pembersihan, Anda dapat melakukan loop melalui all_handlers, dan memanggil removeEventHandler untuk masing-masing. Jangan khawatir tentang memanggil removeEventHandler beberapa kali dengan fungsi yang sama. Itu tidak berbahaya.

Sebagai contoh,

function cleanup(elem) {
    for (let t in elem) if (t.startsWith('on') && elem[t] != null) {
        elem[t] = null;
        console.log('cleanup removed listener from '+elem.nodeName,t);
    } 
    for (let t of elem.all_handlers || []) {
        elem.removeEventListener(t.typ, t.fn, t.opt);
        console.log('cleanup removed listener from '+elem.nodeName,t.typ);
    } 
}

Catatan: untuk IE gunakan Element, bukan EventTarget, dan ubah => menjadi berfungsi, dan berbagai hal lainnya.

John Henckel
sumber
0

Untuk melengkapi jawabannya, berikut adalah contoh dunia nyata dari menghapus peristiwa saat Anda mengunjungi situs web dan tidak memiliki kontrol atas kode HTML dan JavaScript yang dihasilkan.

Beberapa situs web yang mengganggu mencegah Anda untuk menyalin dan menempel nama pengguna pada formulir login, yang dapat dengan mudah dilewati jika onpasteacara tersebut ditambahkan dengan onpaste="return false"atribut HTML. Dalam hal ini kita hanya perlu mengklik kanan pada kolom input, pilih "Inspect element" di browser seperti Firefox dan hapus atribut HTML.

Namun, jika acara tersebut ditambahkan melalui JavaScript seperti ini:

document.getElementById("lyca_login_mobile_no").onpaste = function(){return false};

Kami juga harus menghapus acara melalui JavaScript:

document.getElementById("lyca_login_mobile_no").onpaste = null;

Dalam contoh saya, saya menggunakan ID "lyca_login_mobile_no" karena itu adalah ID input teks yang digunakan oleh situs web yang saya kunjungi.

Cara lain untuk menghapus acara (yang juga akan menghapus semua acara) adalah dengan menghapus node dan membuat yang baru, seperti yang harus kita lakukan jika addEventListenerdigunakan untuk menambahkan acara menggunakan fungsi anonim yang tidak dapat kami hapus removeEventListener. Ini juga dapat dilakukan melalui konsol browser dengan memeriksa elemen, menyalin kode HTML, menghapus kode HTML, dan kemudian menempelkan kode HTML di tempat yang sama.

Itu juga dapat dilakukan lebih cepat dan otomatis melalui JavaScript:

var oldNode = document.getElementById("lyca_login_mobile_no");
var newNode = oldNode.cloneNode(true);
oldNode.parentNode.insertBefore(newNode, oldNode);
oldNode.parentNode.removeChild(oldNode);

Pembaruan: jika aplikasi web dibuat menggunakan kerangka kerja JavaScript seperti Angular, tampaknya solusi sebelumnya tidak berfungsi atau merusak aplikasi. Solusi lain untuk memungkinkan menempelkan adalah menyetel nilai melalui JavaScript:

document.getElementById("lyca_login_mobile_no").value = "username";

Saat ini, saya tidak tahu apakah ada cara untuk menghapus semua validasi formulir dan peristiwa pembatasan tanpa merusak aplikasi yang seluruhnya ditulis dalam JavaScript seperti Angular.

baptx
sumber
0

angular memiliki polyfill untuk masalah ini, Anda dapat memeriksanya. Saya tidak banyak mengerti tapi mungkin itu bisa membantu.

const REMOVE_ALL_LISTENERS_EVENT_LISTENER = 'removeAllListeners';

    proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function () {
        const target = this || _global;
        const eventName = arguments[0];
        if (!eventName) {
            const keys = Object.keys(target);
            for (let i = 0; i < keys.length; i++) {
                const prop = keys[i];
                const match = EVENT_NAME_SYMBOL_REGX.exec(prop);
                let evtName = match && match[1];
                // in nodejs EventEmitter, removeListener event is
                // used for monitoring the removeListener call,
                // so just keep removeListener eventListener until
                // all other eventListeners are removed
                if (evtName && evtName !== 'removeListener') {
                    this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName);
                }
            }
            // remove removeListener listener finally
            this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener');
        }
        else {
            const symbolEventNames = zoneSymbolEventNames$1[eventName];
            if (symbolEventNames) {
                const symbolEventName = symbolEventNames[FALSE_STR];
                const symbolCaptureEventName = symbolEventNames[TRUE_STR];
                const tasks = target[symbolEventName];
                const captureTasks = target[symbolCaptureEventName];
                if (tasks) {
                    const removeTasks = tasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
                if (captureTasks) {
                    const removeTasks = captureTasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
            }
        }
        if (returnTarget) {
            return this;
        }
    };

....

feyzullahyildiz
sumber
Dari mana EVENT_NAME_SYMBOL_REGXasalnya?
Elias Zamaria
0

Anda dapat menambahkan fungsi helper yang menghapus event listener, misalnya

function clearEventListener(element) {
 const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}

cukup kirimkan elemen ke fungsi dan hanya itu ...

Abhilash Narayan
sumber
-1
var div = getElementsByTagName('div')[0]; /* first div found; you can use getElementById for more specific element */
div.onclick = null; // OR:
div.onclick = function(){};

// edit

Saya tidak tahu metode apa yang Anda gunakan untuk melampirkan acara. Untuk addEventListenerAnda dapat menggunakan ini:

div.removeEventListener('click',functionName,false); // functionName is the name of your callback function

lebih detail

Ionuț Staicu
sumber
1
jika saya mengaturnya seperti div.onClick = somethingmaka itu menetapkan onClick acara lain, tetapi yang sebelumnya tetap, jadi saya memiliki sebelumnya dan satu misalnya null ...
Florian Müller
1
Tidak akan berfungsi jika penangan ditambahkan melalui addEventListener().
Felix Kling
@ Florian: Tidak, itu tidak benar. Jika Anda menggunakan metode menambahkan pengendali kejadian ini, Anda hanya dapat memiliki satu pengendali untuk sebuah kejadian. Tapi itu membuat perbedaan jika Anda menggunakan addEventListener()...
Felix Kling
tetapi saya telah melihatnya seperti ini, jadi jika saya menambahkan fungsi sebelumnya lagi, itu akan dipanggil dua kali ... dan, seperti yang dikatakan Felix King, ini tidak berfungsi dengan addEventListener ...
Florian Müller
-1

Mungkin browser akan melakukannya untuk Anda jika Anda melakukan sesuatu seperti:

Salin divdan atributnya dan masukkan sebelum yang lama, lalu pindahkan konten dari yang lama ke yang baru dan hapus yang lama?

Mikrofon
sumber
sekarang itu sebenarnya ide yang bagus, tapi sama sekali tidak berkinerja baik, jika Anda harus melakukan ini untuk sejumlah div antara 1'000 dan 1'000'000 ... Ya, ini adalah proyek besar;)
Florian Müller
6
1 juta div dalam satu halaman? Anda akan mendapatkan masalah lain sebelum ini;) Mungkin menggunakan delegasi acara kemudian ...
Mic
-1

Menghapus semua acara di document :

Satu liner:

for (key in getEventListeners(document)) { getEventListeners(document)[key].forEach(function(c) { c.remove() }) }

Versi cantik:

for (key in getEventListeners(document)) {
  getEventListeners(document)[key].forEach(function(c) {
    c.remove()
  })   
}
Dorian
sumber
-1

Chrome saja

Saat saya mencoba untuk menghapus EventListener dalam pengujian Protractor dan tidak memiliki akses ke Listener dalam pengujian, berikut ini berfungsi untuk menghapus semua event listener dari satu jenis:

function(eventType){
    getEventListeners(window).[eventType].forEach(
        function(e){
            window.removeEventListener(eventType, e.listener)
        }
    );
}

Saya harap ini membantu seseorang karena jawaban sebelumnya menggunakan metode "hapus" yang sejak itu tidak berfungsi lagi.

Filipe Melo
sumber
-1

Salah satu metodenya adalah menambahkan event listener baru yang memanggil e.stopImmediatePropagation ().

Lacho Tomov
sumber