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

Вот моя проблема: возможно ли как-то проверить наличие динамически подключенного прослушивателя событий? Или как я могу проверить состояние свойства onclick (?) В DOM? Я искал в Интернете, как и Stackru, решение, но не повезло. Вот мой 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 for 2", а затем "[ссылка 2]", событие срабатывает хорошо, но "Тестовая ссылка 2" всегда сообщает об ошибке. Может кто-нибудь помочь?

24 ответа

Решение

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

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

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

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

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

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

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

То, что я хотел бы сделать, это создать логическое значение вне вашей функции, которое начинается с 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);
    }
}

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

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

getEventListeners(document.querySelector('your-element-selector'));

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

Snippet:

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>


Хотя прошло более 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;
};

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

Обновление 2022 года:

Я написал служебные методы в TypeScript для присоединения и отсоединения событий на основе этого ответа , но исправленного. Надеюсь, кто-то найдет это полезным.

      export const attachEvent = (
  element: Element,
  eventName: string,
  callback: () => void
) => {
  if (element && eventName && element.getAttribute('listener') !== 'true') {
    element.setAttribute('listener', 'true');
    element.addEventListener(eventName, () => {
      callback();
    });
  }
};

export const detachEvent = (
  element: Element,
  eventName: string,
  callback: () => void
) => {
  if (eventName && element) {
    element.removeEventListener(eventName, callback);
  }
};

Импортируйте его и используйте где угодно, как это

        attachEvent(domElement, 'click', this.myfunction.bind(this));
  detachEvent(domElement, 'click', this.myfunction);

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

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

Fire Fox

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

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

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

Хром

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

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

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

Вот скрипт, который я использовал для проверки наличия динамически подключенного прослушивателя событий. Я использовал jQuery, чтобы прикрепить обработчик события к элементу, а затем вызвать это событие (в данном случае событие "click"). Таким образом, я могу получить и захватить свойства события, которые будут существовать, только если прикреплен обработчик события.

var eventHandlerType;

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

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

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

// Просто возьмите это и назначьте атрибут вне анонимной функции

      const element = document.getElementById('div');
if (element && !element.hasAttribute('listenerOnClick')) {
    element.addEventListener('click', function () {
        const elementClicked = this;
        // fnDoAnything(this); // maybe call a function with the elementClicked...
        console.log('event has been attached');
    });
}
element.setAttribute('listenerOnClick', 'true');

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

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
              var listenerPaste = pasteAreaElm.hasOwnProperty('listenerPaste');
        if (!listenerPaste) {
            pasteAreaElm.addEventListener("paste", onPasteEvent, false);
            pasteAreaElm.listenerPaste = true;
        }

В JavaScript можно перечислить все прослушиватели событий. Вам просто нужно взломать метод прототипа элементов HTML (перед добавлением слушателей).

Это решение будет работать только в браузерах Google Chrome или Chromium.

  1. Щелкните элемент правой кнопкой мыши и выберите «проверить», чтобы открыть инструменты разработчика Chrome.
  2. После того, как инструменты разработчика открыты, переключитесь на вкладку «Прослушиватели событий», и вы увидите все прослушиватели событий, привязанные к элементу.

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

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

item.onclick

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

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

item.hasOwnProperty('onclick')

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

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

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

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

var listenerPresent=false

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

listenerPresent=true

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

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

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

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

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

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

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

injectScript(flagClickHandledElements);

(3) Добавьте «webNavigation» в список «разрешений» в 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');

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

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

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

element.classList.add('attached-listener');

Для этого я написал 3 функции:

      var addEvent = function(object, type, callback)
{
    if (object != null && typeof(object) != 'undefined' && object.addEventListener)
    {
        object.isAttached = typeof object.isAttached == "undefined" ? [] : object.isAttached;
        if (!object.isAttached[type])
        {
            object.isAttached[type] = true;
            object.addEventListener(type, callback, false);
        }
    }
};

var removeEvent = function(object, type, callback)
{
    if (object != null && typeof(object) != "undefined" && typeof object.isAttached != "undefined" && object.isAttached[type])
    {
        object.removeEventListener(type, callback, false);
        object.isAttached[type] = false;
    }
};

var hasEvent = function(object, type, callback)
{
    return object != null && typeof(object) != "undefined" && typeof object.isAttached != "undefined" && object.isAttached[type];
};

Использовать функции просто:

      function mousemove(e)
{
    console.log("x:" + e.clientX + ", y:" + e.clientY);
}

if (hasEvent(window, "mousemove", mousemove))
    console.log('window has "mousemove" event with "mousemove" callback');
else
    console.log('window does not have "mousemove" event with "mousemove" callback');

addEvent(window, "mousemove", mousemove);

if (hasEvent(window, "mousemove", mousemove))
    console.log('window has "mousemove" event with "mousemove" callback');
else
    console.log('window does not have "mousemove" event with "mousemove" callback');
/*
Output
window does not have "mousemove" event with "mousemove"
window has "mousemove" event with "mousemove" callback
The x and y coordinates of mouse as you move it
*/

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

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

Здравствуйте, я предлагаю здесь свой метод, который работает определенным образом, чтобы избежать того, что несколько человек даже прослушивают один и тот же элемент DOM. Мой метод использует JQUERY, но принцип следующий:

После определенной обработки вашего приложения, когда вы должны воспроизвести прослушиватель для элемента DOM, такого как щелчок, мы начинаем с удаления события, чтобы избежать его повторения, и сразу после этого мы воссоздаем его.

Этот принцип следует адаптировать в соответствии с потребностями. Это просто принцип. Я покажу вам, как это сделать в jquery.

      line 1 : $(_ELTDOM_).unbind('click');
line 2 : $(_ELTDOM_).bind('click',myFct);

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

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

Я только что написал сценарий, который позволяет вам достичь этого. Это дает вам две глобальные функции: 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)}();
      document.addEventListener('CustomEvent', delegate_method, {once:true});
Другие вопросы по тегам