Bagaimana cara memeriksa apakah pendengar acara yang dilampirkan secara dinamis ada atau tidak?


105

Inilah masalah saya: apakah mungkin entah bagaimana untuk memeriksa keberadaan event listener yang dilampirkan secara dinamis? Atau bagaimana cara memeriksa status properti "onclick" (?) Di DOM? Saya telah mencari di internet seperti Stack Overflow untuk mendapatkan solusi, tetapi tidak berhasil. Ini html saya:

<a id="link1" onclick="linkclick(event)"> link 1 </a>
<a id="link2"> link 2 </a> <!-- without inline onclick handler -->

Kemudian di Javascript saya lampirkan event listener yang dibuat secara dinamis ke link ke-2:

document.getElementById('link2').addEventListener('click', linkclick, false);

Kode berjalan dengan baik, tetapi semua upaya saya untuk mendeteksi pendengar yang terlampir gagal:

// test for #link2 - dynamically created eventlistener
alert(elem.onclick); // null
alert(elem.hasAttribute('onclick')); // false
alert(elem.click); // function click(){[native code]} // btw, what's this?

jsFiddle ada di sini . Jika Anda mengklik "Add onclick for 2" dan kemudian "[link 2]", peristiwa akan berjalan dengan baik, tetapi "Test link 2" selalu melaporkan false. Adakah yang bisa membantu?


1
Maaf untuk mengatakannya, tetapi tidak mungkin untuk mendapatkan event binding menggunakan metode Anda saat ini: stackoverflow.com/questions/5296858/…
Ivan

1
Anda dapat melakukannya menggunakan alat chrome dev: stackoverflow.com/a/41137585/863115
arufian

Anda dapat melakukannya dengan membuat penyimpanan Anda sendiri yang melacak pendengar. Lihat jawaban saya untuk lebih lanjut.
Angel Politis

Jika tujuannya adalah untuk mencegah event yang sudah ditambahkan ditambahkan lagi maka jawaban yang benar ada disini . Pada dasarnya, jika Anda menggunakan fungsi bernama alih-alih fungsi anonim, pendengar peristiwa identik duplikat akan dibuang dan Anda tidak perlu mengkhawatirkannya.
Syknapse

Jika Anda khawatir memiliki pendengar duplikat, hapus pemroses kejadian saat ini sebelum Anda menambahkannya lagi. Itu tidak sempurna tapi sederhana.
Jacksonkr

Jawaban:


49

Tidak ada cara untuk memeriksa apakah event listener yang dilampirkan secara dinamis ada atau tidak.

Satu-satunya cara untuk melihat apakah pemroses acara dilampirkan adalah dengan melampirkan pemroses acara seperti ini:

elem.onclick = function () { console.log (1) }

Anda kemudian dapat menguji apakah pemroses acara dilampirkan onclickdengan kembali !!elem.onclick(atau yang serupa).


36

Saya melakukan sesuatu seperti itu:

const element = document.getElementById('div');

if (element.getAttribute('listener') !== 'true') {
     element.addEventListener('click', function (e) {
         const elementClicked = e.target;
         elementClicked.setAttribute('listener', 'true');
         console.log('event has been attached');
    });
}

Membuat atribut khusus untuk elemen saat pemroses dilampirkan, lalu memeriksa apakah ada.


5
Ini adalah pendekatan yang saya gunakan di masa lalu. Rekomendasi saya adalah menggunakan struktur sintaks yang sangat spesifik. IE, bukan "pendengar", gunakan acara itu sendiri. Jadi "data-event-click". Ini memberikan beberapa fleksibilitas jika Anda ingin melakukan lebih dari satu acara, dan membuat hal-hal sedikit lebih mudah dibaca.
conrad10781

Ini dengan penambahan @ conrad10781 sepertinya cara yang paling andal dan paling sederhana. Jika karena alasan apa pun elemen tersebut dirender ulang dan pemroses peristiwa terputus, atribut ini juga akan disetel ulang.
Arye Eidelman

25

Apa yang akan saya lakukan adalah membuat Boolean di luar fungsi Anda yang dimulai sebagai FALSE dan disetel ke TRUE ketika Anda melampirkan acara tersebut. Ini akan berfungsi sebagai semacam panji untuk Anda sebelum Anda melampirkan acara lagi. Berikut contoh idenya.

// initial load
var attached = false;

// this will only execute code once
doSomething = function()
{
 if (!attached)
 {
  attached = true;
  //code
 }
} 

//attach your function with change event
window.onload = function()
{
 var txtbox = document.getElementById('textboxID');

 if (window.addEventListener)
 {
  txtbox.addEventListener('change', doSomething, false);
 }
 else if(window.attachEvent)
 {
  txtbox.attachEvent('onchange', doSomething);
 }
}

Ini mengharuskan Anda untuk mengubah kode dari acara melekat melacak apakah itu melekat (mirip dengan ini atau jawaban ini ). Ini tidak akan berfungsi jika Anda tidak mengontrol kode.
pengguna202729


6

tl; dr : Tidak, Anda tidak dapat melakukan ini dengan cara yang didukung secara native.


Satu-satunya cara yang saya tahu untuk mencapai ini adalah dengan membuat objek penyimpanan khusus tempat Anda menyimpan catatan pendengar yang ditambahkan. Sesuatu di sepanjang baris berikut:

/* Create a storage object. */
var CustomEventStorage = [];

Langkah 1: Pertama, Anda akan memerlukan fungsi yang dapat melintasi objek penyimpanan dan mengembalikan rekaman elemen yang diberi elemen (atau false).

/* The function that finds a record in the storage by a given element. */
function findRecordByElement (element) {
    /* Iterate over every entry in the storage object. */
    for (var index = 0, length = CustomEventStorage.length; index < length; index++) {
        /* Cache the record. */
        var record = CustomEventStorage[index];

        /* Check whether the given element exists. */
        if (element == record.element) {
            /* Return the record. */
            return record;
        }
    }

    /* Return false by default. */
    return false;
}

Langkah 2: Kemudian, Anda memerlukan fungsi yang dapat menambahkan pendengar acara, tetapi juga memasukkan pendengar ke objek penyimpanan.

/* The function that adds an event listener, while storing it in the storage object. */
function insertListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found. */
    if (record) {
        /* Normalise the event of the listeners object, in case it doesn't exist. */
        record.listeners[event] = record.listeners[event] || [];
    }
    else {
        /* Create an object to insert into the storage object. */
        record = {
            element: element,
            listeners: {}
        };

        /* Create an array for event in the record. */
        record.listeners[event] = [];

        /* Insert the record in the storage. */
        CustomEventStorage.push(record);
    }

    /* Insert the listener to the event array. */
    record.listeners[event].push(listener);

    /* Add the event listener to the element. */
    element.addEventListener(event, listener, options);
}

Langkah 3: Berkenaan dengan persyaratan sebenarnya dari pertanyaan Anda, Anda memerlukan fungsi berikut untuk memeriksa apakah sebuah elemen telah ditambahkan pendengar acara untuk acara tertentu.

/* The function that checks whether an event listener is set for a given event. */
function listenerExists (element, event, listener) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether a record was found & if an event array exists for the given event. */
    if (record && event in record.listeners) {
        /* Return whether the given listener exists. */
        return !!~record.listeners[event].indexOf(listener);
    }

    /* Return false by default. */
    return false;
}

Langkah 4: Terakhir, Anda memerlukan fungsi yang dapat menghapus pendengar dari objek penyimpanan.

/* The function that removes a listener from a given element & its storage record. */
function removeListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found and, if found, whether the event exists. */
    if (record && event in record.listeners) {
        /* Cache the index of the listener inside the event array. */
        var index = record.listeners[event].indexOf(listener);

        /* Check whether listener is not -1. */
        if (~index) {
            /* Delete the listener from the event array. */
            record.listeners[event].splice(index, 1);
        }

        /* Check whether the event array is empty or not. */
        if (!record.listeners[event].length) {
            /* Delete the event array. */
            delete record.listeners[event];
        }
    }

    /* Add the event listener to the element. */
    element.removeEventListener(event, listener, options);
}

Potongan:


Meskipun lebih dari 5 tahun telah berlalu sejak OP memposting pertanyaan, saya yakin orang-orang yang tersandung di masa depan akan mendapat manfaat dari jawaban ini, jadi silakan memberikan saran atau perbaikan. 😊


Terima kasih untuk solusi ini, mudah dan kokoh. Namun ada satu komentar, ada satu kesalahan kecil. Fungsi "removeListener" memeriksa apakah larik peristiwa kosong atau tidak tetapi ternernya salah. Ini harus mengatakan "if (record.listeners [event] .length! = - 1)".
JPA

5

Anda selalu dapat memeriksa secara manual apakah EventListener Anda ada menggunakan inspektur Chrome misalnya. Di tab Elemen Anda memiliki subtab "Gaya" tradisional dan dekat dengan subtab lain: "Pemroses Acara". Yang akan memberi Anda daftar semua EventListeners dengan elemen tertautnya.


5

Tampaknya aneh bahwa metode ini tidak ada. Apakah sudah waktunya untuk menambahkannya akhirnya?

Jika Anda ingin, Anda dapat melakukan sesuatu seperti berikut:

var _addEventListener = EventTarget.prototype.addEventListener;
var _removeEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.events = {};
EventTarget.prototype.addEventListener = function(name, listener, etc) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    events[name] = [];
  }

  if (events[name].indexOf(listener) == -1) {
    events[name].push(listener);
  }

  _addEventListener(name, listener);
};
EventTarget.prototype.removeEventListener = function(name, listener) {
  var events = EventTarget.prototype.events;

  if (events[name] != null && events[name].indexOf(listener) != -1) {
    events[name].splice(events[name].indexOf(listener), 1);
  }

  _removeEventListener(name, listener);
};
EventTarget.prototype.hasEventListener = function(name) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    return false;
  }

  return events[name].length;
};

3

Berikut skrip yang saya gunakan untuk memeriksa keberadaan event listener yang dilampirkan secara dinamis. Saya menggunakan jQuery untuk melampirkan event handler ke sebuah elemen, kemudian memicu event tersebut (dalam hal ini event 'click'). Dengan cara ini saya bisa mengambil dan menangkap properti acara yang hanya akan ada jika pengendali acara terpasang.

var eventHandlerType;

$('#contentDiv').on('click', clickEventHandler).triggerHandler('click');

function clickEventHandler(e) {
    eventHandlerType = e.type;
}

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}

2
Anda juga dapat menetapkan properti ke elemen yang pada dasarnya menandainya sebagai elemen yang memiliki acara;
Justin

2

Tampaknya tidak ada fungsi lintas browser yang mencari acara yang terdaftar di bawah elemen tertentu.

Namun, dimungkinkan untuk melihat fungsi panggilan balik untuk elemen di beberapa browser menggunakan alat pengembangannya. Ini dapat berguna ketika mencoba untuk menentukan bagaimana sebuah halaman web berfungsi atau untuk kode debug.

Firefox

Pertama, lihat elemen di tab Inspector di dalam alat pengembang. Ini bisa dilakukan:

  • Di halaman dengan mengklik kanan item di halaman web yang ingin Anda periksa dan memilih "Inspect Element" dari menu.
  • Di dalam konsol dengan menggunakan fungsi untuk memilih elemen, seperti document.querySelector , lalu mengklik ikon di samping elemen untuk melihatnya di tab Inspektur .

Jika ada event yang didaftarkan ke elemen, Anda akan melihat tombol yang berisi kata Event di samping elemen. Mengkliknya akan memungkinkan Anda untuk melihat event yang telah terdaftar dengan elemen tersebut. Mengklik panah di samping acara memungkinkan Anda untuk melihat fungsi panggilan balik untuk itu.

Chrome

Pertama, lihat elemen di tab Elemen dalam alat pengembang. Ini bisa dilakukan:

  • Di halaman dengan mengklik kanan item di halaman web yang ingin Anda periksa dan memilih "Periksa" dari menu
  • Di dalam konsol dengan menggunakan fungsi untuk memilih elemen, seperti document.querySelector , klik kanan elemen, dan pilih "Reveal in Elements panel" untuk melihatnya di tab Inspector .

Di dekat bagian jendela yang menunjukkan pohon yang berisi elemen halaman web, seharusnya ada bagian lain dengan tab berjudul "Event Listeners". Pilih untuk melihat acara yang telah didaftarkan ke elemen tersebut. Untuk melihat kode acara tertentu, klik link di sebelah kanannya.

Di Chrome, kejadian untuk elemen juga dapat ditemukan menggunakan fungsi getEventListeners . Namun, berdasarkan pengujian saya, fungsi getEventListeners tidak mencantumkan peristiwa ketika beberapa elemen diteruskan ke sana. Jika Anda ingin menemukan semua elemen di halaman yang memiliki listener dan melihat fungsi callback untuk listener tersebut, Anda dapat menggunakan kode berikut di konsol untuk melakukannya:

var elems = document.querySelectorAll('*');

for (var i=0; i <= elems.length; i++) {
    var listeners = getEventListeners(elems[i]);

    if (Object.keys(listeners).length < 1) {
        continue;
    }

    console.log(elems[i]);

    for (var j in listeners) {
        console.log('Event: '+j);

        for (var k=0; k < listeners[j].length; k++) {
            console.log(listeners[j][k].listener);
        }
    }
}

Harap edit jawaban ini jika Anda mengetahui cara melakukan ini di browser yang diberikan atau di browser lain.


1

Saya baru mengetahuinya dengan mencoba melihat apakah acara saya dilampirkan ....

jika kamu melakukan :

item.onclick

itu akan mengembalikan "null"

tetapi jika Anda melakukannya:

item.hasOwnProperty('onclick')

maka itu adalah "BENAR"

jadi saya pikir ketika Anda menggunakan "addEventListener" untuk menambahkan event handler, satu-satunya cara untuk mengaksesnya adalah melalui "hasOwnProperty". Saya berharap saya tahu mengapa atau bagaimana tetapi sayangnya, setelah meneliti, saya belum menemukan penjelasan.


2
onclickterpisah dari .addEventListener- itu mempengaruhi atribut sementara .addEventListenertidak
Zach Saucier

1

Jika saya mengerti dengan baik, Anda hanya dapat memeriksa apakah pendengar telah diperiksa tetapi tidak pendengar mana yang menjadi penyaji secara khusus.

Jadi beberapa kode ad hoc akan mengisi celah untuk menangani alur pengkodean Anda. Metode praktis akan membuat statevariabel menggunakan. Misalnya, lampirkan pemeriksa pendengar sebagai berikut:

var listenerPresent=false

maka jika Anda menyetel pendengar cukup ubah nilainya:

listenerPresent=true

kemudian di dalam callback eventListener Anda, Anda dapat menetapkan fungsionalitas tertentu di dalam dan dengan cara yang sama, mendistribusikan akses ke fungsionalitas bergantung pada beberapa status sebagai variabel misalnya:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

Hapus saja acara tersebut sebelum menambahkannya:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);

Itu trik yang bagus, tapi saya berasumsi ini hanya bekerja jika Anda memiliki ref ke fungsi yang merupakan salah satu event-listener yang saat ini "ditambahkan". Saya pikir ini jelas merupakan kekurangan dari API standar, jika kita dapat menambahkan event-listener, itu juga harus memungkinkan untuk memeriksa event-listener apa yang telah ditambahkan sejauh ini. Ini hampir seperti event-listener saat ini adalah "variabel hanya tulis" yang tampak seperti konsep yang tidak jelas.
Panu Logic

0

Saya baru saja menulis skrip yang memungkinkan Anda mencapai ini. Ini memberi Anda dua fungsi global: hasEvent(Node elm, String event)dan getEvents(Node elm)yang dapat Anda manfaatkan. Ketahuilah bahwa ini memodifikasi EventTargetmetode prototipe add/RemoveEventListener, dan tidak berfungsi untuk acara yang ditambahkan melalui markup HTML atau sintaks javascriptelm.on_event = ...

Info selengkapnya di GitHub

Demo Langsung

Naskah:

var hasEvent,getEvents;!function(){function b(a,b,c){c?a.dataset.events+=","+b:a.dataset.events=a.dataset.events.replace(new RegExp(b),"")}function c(a,c){var d=EventTarget.prototype[a+"EventListener"];return function(a,e,f,g,h){this.dataset.events||(this.dataset.events="");var i=hasEvent(this,a);return c&&i||!c&&!i?(h&&h(),!1):(d.call(this,a,e,f),b(this,a,c),g&&g(),!0)}}hasEvent=function(a,b){var c=a.dataset.events;return c?new RegExp(b).test(c):!1},getEvents=function(a){return a.dataset.events.replace(/(^,+)|(,+$)/g,"").split(",").filter(function(a){return""!==a})},EventTarget.prototype.addEventListener=c("add",!0),EventTarget.prototype.removeEventListener=c("remove",!1)}();

-1

Anda bisa secara teori piggyback addEventListener dan removeEventListener untuk menambahkan menghapus bendera ke objek 'ini'. Jelek dan saya belum menguji ...

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.