Как проверить, существует ли динамически прикрепленный прослушиватель событий?

105

Вот моя проблема: можно ли как-то проверить наличие динамически прикрепленного прослушивателя событий? Или как я могу проверить статус свойства 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 я прикрепляю динамически созданный прослушиватель событий ко второй ссылке:

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 for 2», а затем «[ссылка 2]», событие сработает нормально, но «Проверить ссылку 2» всегда выдает ошибку. Кто-нибудь может помочь?

Стано
источник
1
Извините, но невозможно получить привязки событий с помощью вашего текущего метода: stackoverflow.com/questions/5296858/…
Иван
1
вы можете сделать это с помощью инструмента Chrome dev: stackoverflow.com/a/41137585/863115
arufian
Вы можете сделать это, создав свое собственное хранилище, которое отслеживает слушателей. См. Мой ответ для получения дополнительной информации.
Angel Politis
Если цель состоит в том, чтобы предотвратить повторное добавление уже добавленного события, тогда правильный ответ здесь . По сути, если вы используете именованную функцию вместо анонимной, дублирующиеся идентичные прослушиватели событий будут отброшены, и вам не нужно о них беспокоиться.
Syknapse
Если вас беспокоят повторяющиеся прослушиватели, удалите текущий прослушиватель событий, прежде чем добавлять его снова. Это не идеально, но все просто.
Jacksonkr

Ответы:

49

Невозможно проверить, существуют ли динамически подключенные прослушиватели событий или нет.

Единственный способ узнать, подключен ли прослушиватель событий, - это подключить такие прослушиватели событий:

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

Затем вы можете проверить, был ли прикреплен прослушиватель событий onclick, возвратив !!elem.onclick(или что-то подобное).

Иван
источник
36

Я сделал что-то подобное:

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

Создание специального атрибута для элемента при присоединении слушателя с последующей проверкой его существования.

Кароль Грабовски
источник
5
Это подход, который я использовал в прошлом. Я бы рекомендовал использовать очень конкретную структуру синтаксиса. IE вместо "слушателя" используйте само событие. Итак, «данные-событие-щелчок». Это обеспечивает некоторую гибкость в случае, если вы хотите провести более одного события, и делает вещи более читабельными.
conrad10781
Это с добавлением @conrad10781 кажется наиболее надежным и простым способом. Если по какой-либо причине элемент повторно визуализируется и прослушиватель событий отключается, этот атрибут также будет сброшен.
Арье Эйдельман
25

Я бы создал логическое значение вне вашей функции, которое начинается как 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. Во-первых, вам понадобится функция, которая может перемещаться по объекту хранилища и возвращать запись элемента, заданного элементом (или 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;
}

Шаг 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 лет с тех пор, как OP опубликовал вопрос, я считаю, что люди, которые наткнутся на него в будущем, извлекут выгоду из этого ответа, поэтому не стесняйтесь вносить предложения или улучшения. 😊

Ангел Политис
источник
Спасибо за это решение, оно простое и надежное. Одно замечание, но есть одна маленькая ошибка. Функция removeListener проверяет, является ли массив событий пустым, но тернарность неверна. Он должен сказать «if (record.listeners [event] .length! = - 1)».
JPA,
5

Вы всегда можете проверить вручную, существует ли ваш EventListener, например, с помощью инспектора Chrome. На вкладке «Элемент» есть традиционная вложенная вкладка «Стили» и рядом с ней еще одна: «Слушатели событий». Это даст вам список всех EventListeners с их связанными элементами.

Поль Леклерк
источник
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;
};
1,21 гигаватт
источник
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');
}
dsmith63
источник
2
Вы также можете назначить свойство элементу, в котором ваш элемент помечает его как элемент, к которому привязано событие;
Джастин
2

Похоже, что нет кроссбраузерной функции, которая ищет события, зарегистрированные для данного элемента.

Однако можно просматривать функции обратного вызова для элементов в некоторых браузерах, используя их инструменты разработки. Это может быть полезно при попытке определить, как работает веб-страница, или для отладки кода.

Fire Fox

Сначала просмотрите элемент на вкладке « Инспектор » в инструментах разработчика. Это можно сделать:

  • На странице , щелкнув правой кнопкой мыши элемент на веб-странице, который вы хотите проверить, и выбрав «Проверить элемент» в меню.
  • В консоли , используя функцию для выбора элемента, например document.querySelector , а затем щелкнув значок рядом с элементом, чтобы просмотреть его на вкладке « Инспектор ».

Если для элемента были зарегистрированы какие-либо события, вы увидите кнопку, содержащую слово Событие рядом с элементом . Щелчок по нему позволит вам увидеть события, которые были зарегистрированы с элементом. Щелкнув стрелку рядом с событием, вы можете просмотреть для него функцию обратного вызова.

Хром

Сначала просмотрите элемент на вкладке « Элементы » в инструментах разработчика. Это можно сделать:

  • На странице , щелкнув правой кнопкой мыши элемент на веб-странице, который вы хотите проверить, и выбрав «Проверить» в меню.
  • В консоли с помощью функции для выбора элемента, такой как 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

он вернет "нуль"

но если вы это сделаете:

item.hasOwnProperty('onclick')

тогда это "ИСТИНА"

поэтому я думаю, что когда вы используете addEventListener для добавления обработчиков событий, единственный способ получить к нему доступ - через hasOwnProperty. Хотел бы я знать, почему и как, но, увы, после исследования я не нашел объяснения.

Каринлинчин
источник
2
onclickотдельно от .addEventListener- это влияет на атрибут, а .addEventListenerне на
Zach Saucier
1

Если я хорошо понимаю, вы можете только проверить, был ли проверен слушатель, но не то, какой именно слушатель является докладчиком.

Таким образом, некоторый специальный код восполнит пробел для обработки вашего потока кодирования. Практический метод - создать stateпеременные using. Например, прикрепите программу проверки слушателя следующим образом:

var listenerPresent=false

тогда, если вы установите слушателя, просто измените значение:

listenerPresent=true

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

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true
Webwoman
источник
1

Просто удалите событие перед его добавлением:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
Entretoize
источник
Это хороший трюк, но я предполагаю, что он работает только в том случае, если у вас есть ссылка на функцию, которая является одним из "добавленных" в настоящее время прослушивателей событий. Я думаю, что это явно недостаток стандартного API, если мы можем добавить какой-либо прослушиватель событий, также должна быть возможность проверить, какие прослушиватели событий были добавлены на данный момент. Это похоже на то, что прослушиватели событий в настоящее время «записывают только переменные», что кажется неясным понятием.
Panu Logic,
0

Я только что написал сценарий, который позволяет вам этого добиться. Он предоставляет вам две глобальные функции: hasEvent(Node elm, String event)и getEvents(Node elm)которые вы можете использовать. Имейте в виду, что он изменяет EventTargetметод прототипа add/RemoveEventListenerи не работает для событий, добавленных с помощью разметки HTML или синтаксиса javascript дляelm.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

Теоретически вы можете использовать addEventListener и removeEventListener для добавления флага удаления к объекту this. Ugly и я не тестировал ...

кофифус
источник