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

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


person Stano    schedule 12.07.2012    source источник
comment
К сожалению, получить привязки событий с помощью вашего текущего метода невозможно: stackoverflow.com/questions/5296858/   -  person Ivan    schedule 12.07.2012
comment
вы можете сделать это с помощью инструмента Chrome Dev: stackoverflow.com/a/41137585/863115   -  person arufian    schedule 02.02.2017
comment
Вы можете сделать это, создав свое собственное хранилище, которое отслеживает слушателей. Дополнительную информацию см. В моем ответе.   -  person Angel Politis    schedule 28.11.2017
comment
Если цель состоит в том, чтобы предотвратить повторное добавление уже добавленного события, тогда правильный ответ здесь. По сути, если вы используете именованную функцию вместо анонимной, дублирующиеся идентичные прослушиватели событий будут отброшены, и вам не нужно о них беспокоиться.   -  person Syknapse    schedule 23.10.2019
comment
Если вас беспокоят повторяющиеся прослушиватели, удалите текущий прослушиватель событий, прежде чем добавлять его снова. Это не идеально, но все просто.   -  person Jacksonkr    schedule 13.11.2019


Ответы (17)


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

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

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

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

person Ivan    schedule 12.07.2012

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

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

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

person Karol Grabowski    schedule 16.11.2017
comment
Это подход, который я использовал в прошлом. Я бы порекомендовал использовать очень специфическую структуру синтаксиса. IE вместо слушателя используйте само событие. Итак, данные-событие-щелчок. Это обеспечивает некоторую гибкость в случае, если вы хотите провести более одного события, и делает вещи более читабельными. - person conrad10781; 25.01.2018
comment
Это с добавлением @conrad10781 кажется наиболее надежным и простым способом. Если по какой-либо причине элемент повторно визуализируется и приемник событий отключается, этот атрибут также будет сброшен. - person Arye Eidelman; 25.06.2019

Я бы создал логическое значение вне вашей функции, которое начинается как 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);
 }
}
person Cesar    schedule 16.01.2013
comment
Для этого вам потребуется изменить код прикрепленного события, чтобы отслеживать, прикреплено ли оно (аналогично this или этот ответ). Это не сработает, если вы не будете контролировать код. - person user202729; 20.11.2018
comment
В моем случае у пользователя может быть одна и та же открытая страница с разными вкладками. Это работает, только если событие будет прикреплено только в одном месте на одной вкладке. Логическое значение не может быть синхронизировано с тем, прикреплено событие или нет, потому что мы просто предполагаем, что это не так, когда это загружается. - person ceckenrode; 17.04.2021

Возможный дубликат: Проверьте, есть ли у элемента есть прослушиватель событий. Нет jQuery. Здесь мой ответ.

В основном вот трюк для браузера Chromium (Chrome):

getEventListeners(document.querySelector('your-element-selector'));
person arufian    schedule 04.07.2018
comment
Точнее, здесь: stackoverflow.com/a/41137585/1431728. - person JohnK; 15.04.2020

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

Фрагмент:

window.onload = function () {
  var
    /* Cache the test element. */
    element = document.getElementById("test"),

    /* Create an event listener. */
    listener = function (e) {
      console.log(e.type + "triggered!");
    };

  /* Insert the listener to the element. */
  insertListener(element, "mouseover", listener);

  /* Log whether the listener exists. */
  console.log(listenerExists(element, "mouseover", listener));

  /* Remove the listener from the element. */
  removeListener(element, "mouseover", listener);

  /* Log whether the listener exists. */
  console.log(listenerExists(element, "mouseover", listener));
};
<!-- Include the Custom Event Storage file -->
<script src = "https://cdn.rawgit.com/angelpolitis/custom-event-storage/master/main.js"></script>

<!-- A Test HTML element -->
<div id = "test" style = "background:#000; height:50px; width: 50px"></div>


Хотя с тех пор, как OP опубликовал вопрос, прошло более 5 лет, я считаю, что люди, которые натолкнутся на него в будущем, извлекут пользу из этого ответа, поэтому не стесняйтесь вносить предложения или улучшения. ????

person Angel Politis    schedule 21.11.2017
comment
Спасибо за это решение, оно простое и надежное. Одно замечание, но есть одна маленькая ошибка. Функция removeListener проверяет, является ли массив событий пустым, но тернарный элемент неверен. Он должен сказать if (record.listeners [event] .length! = - 1). - person JPA; 23.01.2020

Кажется странным, что такого метода не существует. Не пора ли его наконец добавить?

Если бы вы хотели, вы могли бы что-то вроде следующего:

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;
};
person 1.21 gigawatts    schedule 28.10.2019

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

person Paul Leclerc    schedule 12.05.2016

Вот сценарий, который я использовал для проверки существования динамически прикрепленного прослушивателя событий. Я использовал 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');
}
person dsmith63    schedule 16.05.2014
comment
Вы также можете присвоить свойство элементу, в котором ваш элемент помечает его как элемент, к которому привязано событие; - person Justin; 27.09.2014

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

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

Firefox

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

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

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

Chrome

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

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

Измените этот ответ, если вы знаете, как это сделать в данных браузерах или в других браузерах.

person Dave F    schedule 29.09.2018

Я только что узнал об этом, попробовав посмотреть, прикреплено ли мое мероприятие ....

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

item.onclick

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

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

item.hasOwnProperty('onclick')

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

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

person carinlynchin    schedule 29.04.2015
comment
onclick отделен от .addEventListener - он влияет на атрибут, а .addEventListener - нет - person Zach Saucier; 15.06.2016

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

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

var listenerPresent=false

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

listenerPresent=true

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

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true
person Webwoman    schedule 29.05.2019

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

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

Я написал расширение Chrome, которое должно было определять, какие элементы на странице реагируют на клики. Вот как я это сделал:

(1) В manifest.json установите для атрибута run_at значение document_start. (Нам нужно внедрить скрипт до того, как страница начнет работать.)

(2) В сценарий содержимого добавьте небольшой код для внедрения сценария на страницу, который переопределит EventTarget.prototype.addEventListener, чтобы пометить все элементы, которым динамически назначаются прослушиватели щелчков:

let flagClickHandledElements = function() {
    let oldEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(event_name, handler_func) {
        if (event_name === 'click') {
            if (this.setAttribute) {
                this.setAttribute('data-has_click_handler', true);
            }
        }
        if (oldEventListener)
            oldEventListener(event_name, handler_func);
    }
}

function injectScript(func) {
    let codeString = '(' + func + ')();';
    let script = document.createElement('script');
    script.textContent = codeString;
    (document.head||document.documentElement).appendChild(script);
}

injectScript(flagClickHandledElements);

(3) Добавьте веб-навигацию в список разрешений в manifest.json.

(4) Добавьте код в фоновый скрипт, чтобы уведомить скрипт содержимого, когда страница загружена:

function onPageDoneLoading(details)
{
    chrome.tabs.sendMessage(details.tabId, {"action": "doneloading"});
}

chrome.webNavigation.onCompleted.addListener(onPageDoneLoading);

(5) Когда страница завершает загрузку, заставьте ваш контентный скрипт внедрить на страницу другой скрипт, который сканирует все элементы на странице в поисках обработчиков onclick в старом стиле:

let gatherOldStyleClickHandledElements = function() {
    let all_elements = document.getElementsByTagName("*");
    for (let i = 0; i < all_elements.length; i++) {
        let el = all_elements[i];
        if (el.setAttribute && el.onclick) {
            el.setAttribute('data-has_click_handler', true);
        }
    }
}

function onReceiveMessage(request) {
    if (request.action === 'doneloading') {
        injectScript(gatherOldStyleClickHandledElements);
    } else {
        console.log('Unrecognized message');
    }

    return Promise.resolve("Dummy response to keep the console quiet");
}

(6) Наконец, вы можете протестировать элемент в своем сценарии содержимого, чтобы увидеть, есть ли у него такой обработчик кликов:

if (el.getAttribute('data-has_click_handler'))
    console.log('yep, this element has a click handler');
person Mike Gashler    schedule 15.04.2021
comment
Я бы с удовольствием посмотрел на это хромированное расширение! Я думаю, у нас может быть такая же идея - person Devin Rhode; 02.05.2021
comment
Увы, я создал его для работы, поэтому творческая часть не принадлежит мне, но я попытался поделиться всеми техническими деталями, указанными выше. - person Mike Gashler; 03.05.2021

Обычно я прикрепляю класс к элементу, а затем проверяю, существует ли этот класс следующим образом:

let element = document.getElementsById("someElement");

if(!element.classList.contains('attached-listener'))
   element.addEventListener("click", this.itemClicked);

element.classList.add('attached-listener');
person Pedro Marthon    schedule 25.12.2020

Что-то вроде этого должно помочь в документе:

var listeners = window.getEventListeners(document);
Object.keys(listeners).forEach(event => {
    console.log(event, listeners[event]);
});

Или с помощью селекторов:

getAllEventListeners = function(el) {
 var allListeners = {}, listeners;

 while(el) {
  listeners = getEventListeners(el);

  for(event in listeners) {
   allListeners[event] = allListeners[event] || [];
   allListeners[event].push({listener: listeners[event], element: el});  
  }

  el = el.parentNode;
 }

 return allListeners;
}
person Vladyslav Hrehul    schedule 29.03.2021

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

Дополнительная информация на GitHub

Live Demo

Скрипт:

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)}();
person Gaurang Tandon    schedule 10.06.2015

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

person kofifus    schedule 10.05.2018