Как да проверя дали динамично прикачения слушател на събития съществува или не?

Ето моят проблем: възможно ли е по някакъв начин да се провери съществуването на динамично прикачен слушател на събития? Или как мога да проверя състоянието на свойството 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 е тук. Ако щракнете върху Add onclick for 2 и след това [link 2], събитието се задейства добре, но Test link 2 винаги отчита невярно. може ли някой да помогне


person Stano    schedule 12.07.2012    source източник
comment
Съжалявам, че трябва да го кажа, но е невъзможно да получите обвързванията за събития, като използвате текущия си метод: stackoverflow.com/questions/5296858/   -  person Ivan    schedule 12.07.2012
comment
можете да го направите с помощта на инструмента за разработка на chrome: 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

Това, което бих направил, е да създам Boolean извън вашата функция, която започва като 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
Това изисква да промените кода на прикаченото събитие, за да следите дали е прикачено (подобно на това или този отговор). Няма да работи, ако не контролирате кода. - 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>


Въпреки че са изминали повече от 5 години, откакто OP публикува въпроса, вярвам, че хората, които се натъкнат на него в бъдеще, ще се възползват от този отговор, така че не се колебайте да правите предложения или подобрения в него. ????

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

Първо вижте елемента в раздела Инспектор в инструментите за разработчици. Това може да се направи:

  • На страницата, като щракнете с десния бутон върху елемента на уеб страницата, който искате да проверите, и изберете „Проверка на елемент“ от менюто.
  • В конзолата, като използвате функция за избиране на елемента, като например document.querySelector, и след това щракнете върху иконата до елемента, за да го видите в Инспектора< /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

ще върне "null"

но ако го направите:

item.hasOwnProperty('onclick')

тогава е "ВЯРНО"

така че мисля, че когато използвате "addEventListener", за да добавите манипулатори на събития, единственият начин за достъп до него е чрез "hasOwnProperty". Иска ми се да знам защо или как, но уви, след проучване не намерих обяснение.

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

Ако разбирам добре, можете да проверите само дали слушател е проверен, но не и кой конкретно слушател е презентатор.

Така че някакъв ad hoc код ще запълни празнината, за да се справи с вашия кодиращ поток. Практичен метод би бил да се създаде state с помощта на променливи. Например, прикачете проверка на слушател, както следва:

var listenerPresent=false

тогава, ако зададете слушател, просто променете стойността:

listenerPresent=true

след това във вашето обратно извикване на 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) Добавете 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');
person Mike Gashler    schedule 15.04.2021
comment
Бих искал да разгледам това разширение за chrome! Мисля, че може да имаме същата идея - 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

Демо на живо

Скрипт:

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, за да добавите премахване на флаг към обекта „този“. Грозни и аз не съм тествал...

person kofifus    schedule 10.05.2018