Як перевірити, чи існує динамічно прикріплений прослуховувач подій чи ні?


103

Ось моя проблема: чи можна якось перевірити наявність динамічно приєднаного прослуховувача подій? Або як я можу перевірити статус властивості "onclick" (?) У DOM? Я шукав рішення в Інтернеті так само, як Stack Overflow, але не везе. Ось мій html:

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

Потім у Javascript я приєдную динамічно створений прослуховувач подій до 2-го посилання:

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

Код працює добре, але всі мої спроби виявити приєднаний прослуховувач не вдаються:

// 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 тут . Якщо натиснути "Додати onclick для 2", а потім "[посилання 2]", подія спрацьовує добре, але "Тестове посилання 2" завжди повідомляє про помилку. Хтось може допомогти?


1
Мені шкода сказати, але неможливо отримати прив’язку подій за допомогою вашого поточного методу: stackoverflow.com/questions/5296858/…
Іван,

1
Ви можете зробити це з допомогою інструменту хром Dev: stackoverflow.com/a/41137585/863115
arufian

Ви можете зробити це, створивши власне сховище, яке відстежує слухачів. Більше див. У моїй відповіді .
Angel Politis

Якщо метою є запобігти повторному додаванню події, яка вже була додана, тоді вірна відповідь тут . В основному, якщо ви використовуєте іменовану функцію замість анонімної, дублікати ідентичних прослуховувачів подій буде відкинуто, і вам не потрібно турбуватися про них.
Сикнапсе

Якщо вас турбує наявність повторюваних прослуховувачів, видаліть поточний прослуховувач подій, перш ніж додавати його знову. Це не ідеально, але просто.
Джексонкр

Відповіді:


49

Немає можливості перевірити, чи існують динамічно прикріплені прослуховувачі подій чи ні.

Єдиний спосіб побачити, чи приєднаний прослуховувач подій, - це підключити прослуховувачі подій так:

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

Потім ви можете перевірити, чи був приєднаний слухач події onclick, повернувшись !!elem.onclick(або щось подібне).


33

Я зробив щось подібне:

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

Створення спеціального атрибута для елемента, коли приєднано слухач, а потім перевірка, чи він існує.


4
Це підхід, який я застосовував у минулому. Я рекомендую використовувати дуже специфічну структуру синтаксису. IE замість "listener", використовуйте саму подію. Тож "дані-подія-клацання". Це забезпечує певну гнучкість у випадку, коли ви хочете зробити більше однієї події, і робить речі трохи більш читабельними.
conrad10781

Це з додаванням @ conrad10781 здається найбільш надійним і простим способом. Якщо з якої-небудь причини елемент буде повторно відтворений, а прослуховувач подій відключений, цей атрибут також буде скинуто.
Ар'є Ейдельмана

23

Що б я зробив, це створити логічне значення за межами вашої функції, яке починається як FALSE і має значення TRUE, коли ви приєднуєте подію. Це могло б служити для вас якимось прапором перед тим, як прикріпити подію знову. Ось приклад ідеї.

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

Це вимагає від вас змінити код вкладеної події, щоб відстежувати, чи вона вкладена (подібно до цієї чи цієї відповіді ). Це не спрацює, якщо ви не керуєте кодом.
user202729


6

tl; dr : Ні, ви не можете зробити це жодним чином підтримуваним способом.


Єдиний спосіб, яким я знаю, щоб досягти цього, - це створити власний об’єкт зберігання, де ви ведете запис про додані слухачі. Щось у таких напрямках:

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

Крок 1: По-перше, вам знадобиться функція, яка може обходити об'єкт зберігання та повертати запис елемента, якому даний елемент (або помилковий).

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

Крок 2: Потім вам знадобиться функція, яка може додати прослуховувач подій, але також вставити прослуховувач до об’єкта зберігання.

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

Крок 3: Що стосується фактичної вимоги вашого запитання, то вам знадобиться наступна функція, щоб перевірити, чи додано до елемента прослуховувач події для зазначеної події.

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

Крок 4: Нарешті, вам знадобиться функція, яка може видалити слухач із об’єкта зберігання.

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

Фрагмент:


Незважаючи на те, що минуло більше 5 років з моменту публікації цього запитання, я вважаю, що люди, які натрапляють на нього в майбутньому, отримають вигоду від цієї відповіді, тому сміливо робіть пропозиції чи вдосконалення. 😊


Дякуємо за це рішення, воно просто та надійне. Хоча одне зауваження, є одна маленька помилка. Функція "removeListener" перевіряє, чи масив подій порожній чи ні, але тернар неправильний. У ньому повинно бути написано "if (record.listeners [event] .length! = - 1)".
JPA

5

Ви завжди можете перевірити вручну, чи існує ваш EventListener, наприклад, використовуючи інспектор Chrome. На вкладці Element у вас є традиційна підвкладка "Стилі", а поруч із нею ще одна: "Слухачі подій". Що дасть вам список усіх прослуховувачів подій з пов’язаними елементами.


5

Дивно, що цього методу не існує. Чи пора додати його нарешті?

Якщо б ви хотіли, ви могли б щось на зразок наступного:

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

Ось сценарій, за допомогою якого я перевіряв наявність динамічно приєднаного прослуховувача подій. Я використовував jQuery, щоб приєднати обробник події до елемента, а потім запустити цю подію (в даному випадку подія "клацніть"). Таким чином я можу отримати та захопити властивості події, які існуватимуть лише за умови приєднання обробника події.

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
Ви також можете призначити властивість елементу, який ваш елемент в основному позначає як елемент, до якого приєднана подія;
Justin

2

Здається, немає функції перехресного браузера, яка здійснює пошук подій, зареєстрованих під даним елементом.

Однак можна переглянути функції зворотного виклику елементів у деяких браузерах, використовуючи їх засоби розробки. Це може бути корисно при спробі визначити, як функціонує веб-сторінка, або для налагодження коду.

Firefox

Спочатку перегляньте елемент на вкладці Інспектор в інструментах розробника. Це можна зробити:

  • На сторінці , клацнувши правою кнопкою миші на елементі веб-сторінки, який потрібно перевірити, та вибравши в меню пункт "Перевірити елемент".
  • У консолі за допомогою функції виберіть елемент, наприклад document.querySelector , а потім клацніть піктограму поруч із елементом, щоб переглянути його на вкладці Inspector .

Якщо будь-які події були зареєстровані в елементі, ви побачите кнопку, що містить слово Подія поруч з елементом. Натиснувши його, ви зможете побачити події, які були зареєстровані з елементом. Натискання стрілки біля події дозволяє переглянути функцію зворотного дзвінка для неї.

Хром

Спочатку перегляньте елемент на вкладці Елементи в інструментах розробника. Це можна зробити:

  • На сторінці , клацнувши правою кнопкою миші на елементі веб-сторінки, який потрібно перевірити, та вибравши в меню пункт "Перевірити"
  • У консолі за допомогою функції вибору елемента, наприклад, document.querySelector , клацніть правою кнопкою миші на елементі та виберіть «Розкрити на панелі елементів», щоб переглянути його на вкладці Інспектор .

Біля розділу вікна, що показує дерево, що містить елементи веб-сторінки, повинен бути інший розділ із вкладкою "Слухачі подій". Виберіть його, щоб переглянути події, які були зареєстровані в елементі. Щоб побачити код даної події, натисніть посилання праворуч від неї.

У Chrome події для елемента також можна знайти за допомогою функції getEventListeners . Однак, на основі моїх тестів, функція getEventListeners не перелічує подій, коли до неї передано кілька елементів. Якщо ви хочете знайти всі елементи на сторінці, які мають прослуховувачі, і переглянути функції зворотного виклику для цих прослуховувачів, ви можете використати такий код у консолі для цього:

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

Будь ласка, відредагуйте цю відповідь, якщо ви знаєте способи зробити це в певних браузерах або інших браузерах.


1

Я щойно з’ясував це, спробувавши з’ясувати, чи додано мою подію ....

Якщо ти зробиш :

item.onclick

він поверне "null"

але якщо ви це зробите:

item.hasOwnProperty('onclick')

тоді це "ІСТИНА"

тому я думаю, що коли ви використовуєте "addEventListener" для додавання обробників подій, єдиний спосіб отримати до нього доступ - через "hasOwnProperty". Я хотів би знати, чому і як, але, на жаль, після досліджень я не знайшов пояснення.


2
onclickокремо від .addEventListener- він впливає на атрибут, тоді як .addEventListenerні
Зак Сосьє

1

Якщо я добре розумію, ви можете лише перевірити, чи перевірено слухача, але не який саме слухач є ведучим.

Отже, якийсь спеціальний код заповнює прогалину для обробки вашого кодування. Практичним методом було б створення stateзмінних, що використовують. Наприклад, приєднайте перевірку слухача наступним чином:

var listenerPresent=false

тоді, якщо ви встановите слухача, просто змініть значення:

listenerPresent=true

тоді всередині зворотного виклику вашого eventListener ви можете призначити певні функціональні можливості всередині і таким же чином розподілити доступ до функціональних можливостей залежно від певного стану як змінну, наприклад:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

Просто видаліть подію перед додаванням:

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

Це хороший фокус, але я припускаю, що він працює, лише якщо у вас є посилання на функцію, яка є одним із "доданих" на даний момент прослуховувачів подій. Я думаю, що це явно недолік стандартного API, якщо ми можемо додати будь-який прослуховувач подій, також повинна бути можливість перевірити, які прослуховувачі подій були додані до цього часу. Це майже так, як прослуховувачі подій в даний час "записують лише змінні", що здається незрозумілим поняттям.
Panu Logic

0

Я щойно написав сценарій, який дозволяє вам цього досягти. Це дає вам дві глобальні функції: hasEvent(Node elm, String event)і getEvents(Node elm)які ви можете використовувати. Майте на увазі, що він модифікує EventTargetметод прототипу add/RemoveEventListenerі не працює для подій, доданих за допомогою розмітки HTML або синтаксису JavaScriptelm.on_event = ...

Більше інформації на GitHub

Демонстраційна демонстрація

Сценарій:

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

Теоретично, ви можете додати piggyback addEventListener та removeEventListener, щоб додати прапорець видалення до об'єкта 'this'. Некрасиво і я не тестував ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.