Jak sprawdzić, czy istnieje dynamicznie dołączany detektor zdarzeń?


105

Oto mój problem: czy można w jakiś sposób sprawdzić, czy istnieje dynamicznie dołączany detektor zdarzeń? Albo jak mogę sprawdzić stan właściwości „onclick” (?) W DOM? Szukałem rozwiązania w Internecie, podobnie jak Stack Overflow, ale bez powodzenia. Oto mój html:

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

Następnie w Javascript dołączam dynamicznie utworzony detektor zdarzeń do drugiego linku:

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

Kod działa dobrze, ale wszystkie moje próby wykrycia tego dołączonego nasłuchiwania kończą się niepowodzeniem:

// 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 jest tutaj . Jeśli klikniesz „Dodaj onclick dla 2”, a następnie „[link 2]”, zdarzenie zostanie uruchomione dobrze, ale „Testuj link 2” zawsze zgłasza fałsz. Czy ktoś może pomóc?


1
Przykro mi to mówić, ale nie można uzyskać powiązań zdarzeń przy użyciu obecnej metody: stackoverflow.com/questions/5296858/ ...
— Ivan

1
możesz to zrobić za pomocą narzędzia chrome dla
— deweloperów

Możesz to zrobić, tworząc własną pamięć, która śledzi słuchaczy. Zobacz moją odpowiedź po więcej.
— Angel Politis

Jeśli celem jest zapobieżenie ponownemu dodaniu dodanego już wydarzenia, to właściwa odpowiedź jest tutaj . Zasadniczo, jeśli używasz nazwanej funkcji zamiast anonimowej, zduplikowane identyczne nasłuchiwania zdarzeń zostaną odrzucone i nie musisz się o nie martwić.
— Syknapse

Jeśli obawiasz się posiadania zduplikowanych detektorów, usuń bieżący detektor zdarzeń, zanim dodasz go ponownie. Nie jest doskonały, ale jest prosty.
— Jacksonkr

Odpowiedzi:


49

Nie ma możliwości sprawdzenia, czy istnieją dynamicznie dołączone nasłuchiwania zdarzeń.

Jedynym sposobem sprawdzenia, czy odbiornik zdarzeń jest dołączony, jest dołączenie detektorów zdarzeń w następujący sposób:

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

Następnie możesz sprawdzić, czy odbiornik zdarzeń został dołączony onclick, zwracając !!elem.onclick(lub coś podobnego).


36

Zrobiłem coś takiego:

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

Utworzenie specjalnego atrybutu dla elementu, gdy jest dołączony detektor, a następnie sprawdzenie, czy istnieje.


5
To jest podejście, które stosowałem w przeszłości. Moim zaleceniem byłoby użycie bardzo specyficznej struktury składni. IE zamiast „listener” użyj samego zdarzenia. Czyli „data-event-click”. Zapewnia to pewną elastyczność w przypadku, gdy chcesz zrobić więcej niż jedno wydarzenie, i sprawia, że ​​wszystko jest bardziej czytelne.
— conrad10781

To z dodaniem @ conrad10781 wydaje się najbardziej niezawodnym i najprostszym sposobem. Jeśli z jakiegoś powodu element zostanie ponownie wyrenderowany, a detektor zdarzeń zostanie odłączony, ten atrybut również zostanie zresetowany.
— Arye Eidelman

25

To, co zrobiłbym, to utworzenie wartości logicznej poza twoją funkcją, która zaczyna się jako FALSE i jest ustawiana na TRUE po dołączeniu zdarzenia. Będzie to służyć jako flaga przed ponownym załączeniem wydarzenia. Oto przykład pomysłu.

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

Wymaga to zmiany kodu dołączonego zdarzenia, aby śledzić, czy jest ono dołączone (podobnie do tej lub tej odpowiedzi ). Nie zadziała, jeśli nie kontrolujesz kodu.
— user202729


6

tl; dr : Nie, nie możesz tego zrobić w żaden natywnie obsługiwany sposób.


Jedynym sposobem, jaki znam, aby to osiągnąć, byłoby utworzenie niestandardowego obiektu pamięci, w którym będziesz przechowywać rejestr dodanych słuchaczy. Coś w następujący sposób:

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

Krok 1: Po pierwsze, będziesz potrzebować funkcji, która może przejść przez obiekt pamięci i zwrócić rekord elementu podanego elementu (lub 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;
}

Krok 2: Następnie będziesz potrzebować funkcji, która może dodać odbiornik zdarzeń, ale także wstawić go do obiektu pamięci.

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

Krok 3: Jeśli chodzi o faktyczne wymaganie twojego pytania, będziesz potrzebować następującej funkcji, aby sprawdzić, czy element został dodany jako detektor zdarzeń dla określonego zdarzenia.

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

Krok 4: Na koniec będziesz potrzebować funkcji, która może usunąć nasłuchiwanie z obiektu pamięci.

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

Skrawek:


Chociaż minęło ponad 5 lat od opublikowania tego pytania w PO, uważam, że osoby, które natkną się na to pytanie w przyszłości, skorzystają na tej odpowiedzi, więc zachęcamy do zgłaszania sugestii lub ulepszeń do niej. 😊


Dziękuję za to rozwiązanie, jest proste i solidne. Jedna uwaga, jest jeden mały błąd. Funkcja „removeListener” sprawdza, czy tablica zdarzeń jest pusta, czy nie, ale trójskładnik jest niepoprawny. Powinien być napisany „if (record.listeners [event] .length! = - 1)”.
— JPA

5

Zawsze możesz sprawdzić ręcznie, czy Twój EventListener istnieje, na przykład za pomocą inspektora Chrome. W zakładce Element znajduje się tradycyjna karta podrzędna „Style”, a obok niej kolejna: „Detektory zdarzeń”. Co da ci listę wszystkich EventListenerów z połączonymi z nimi elementami.


5

Wydaje się dziwne, że ta metoda nie istnieje. Czy czas wreszcie to dodać?

Jeśli chcesz, możesz coś takiego:

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

Oto skrypt, którego użyłem do sprawdzenia, czy istnieje dynamicznie dołączony detektor zdarzeń. Użyłem jQuery, aby dołączyć procedurę obsługi zdarzeń do elementu, a następnie wyzwolić to zdarzenie (w tym przypadku zdarzenie „kliknięcie”). W ten sposób mogę pobrać i przechwycić właściwości zdarzenia, które będą istnieć tylko wtedy, gdy jest dołączony program obsługi zdarzeń.

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
Możesz również przypisać właściwość elementowi, który jest Twoim elementem, po prostu oznaczając go jako element, do którego jest dołączone zdarzenie;
— Justin

2

Wygląda na to, że nie istnieje funkcja między przeglądarkami, która wyszukuje zdarzenia zarejestrowane w ramach danego elementu.

Możliwe jest jednak przeglądanie funkcji wywołania zwrotnego dla elementów w niektórych przeglądarkach za pomocą ich narzędzi programistycznych. Może to być przydatne podczas próby ustalenia, jak działa strona internetowa lub do debugowania kodu.

Firefox

Najpierw wyświetl element na karcie Inspektor w narzędziach programistycznych. To może być skończone:

  • Na stronie , klikając prawym przyciskiem myszy element na stronie internetowej, który chcesz sprawdzić i wybierając z menu opcję „Sprawdź element”.
  • W konsoli za pomocą funkcji do wybrania elementu, na przykład document.querySelector , a następnie kliknięcia ikony obok elementu, aby wyświetlić go na karcie Inspektor .

Jeśli jakieś zdarzenia zostały zarejestrowane w elemencie, zobaczysz przycisk zawierający słowo Event obok elementu . Kliknięcie go pozwoli Ci zobaczyć zdarzenia, które zostały zarejestrowane z elementem. Kliknięcie strzałki obok zdarzenia umożliwia wyświetlenie odpowiadającej mu funkcji wywołania zwrotnego.

Chrom

Najpierw wyświetl element na karcie Elementy w narzędziach programistycznych. To może być skończone:

  • Na stronie , klikając prawym przyciskiem myszy element na stronie internetowej, który chcesz sprawdzić i wybierając „Sprawdź” z menu
  • W konsoli za pomocą funkcji do wybrania elementu, na przykład document.querySelector , kliknięcie elementu prawym przyciskiem myszy i wybranie opcji „Pokaż w panelu Elementy”, aby wyświetlić go na karcie Inspektor .

W pobliżu sekcji okna wyświetlającego drzewo zawierające elementy strony internetowej powinna znajdować się kolejna sekcja z zakładką zatytułowaną „Detektory zdarzeń”. Wybierz, aby zobaczyć zdarzenia, które zostały zarejestrowane w elemencie. Aby zobaczyć kod dla danego wydarzenia, kliknij link po jego prawej stronie.

W przeglądarce Chrome zdarzenia dla elementu można również znaleźć za pomocą funkcji getEventListeners . Jednak na podstawie moich testów funkcja getEventListeners nie wyświetla zdarzeń, gdy przekazywanych jest do niej wiele elementów. Jeśli chcesz znaleźć wszystkie elementy na stronie, które mają detektory, i wyświetlić funkcje wywołania zwrotnego dla tych detektorów, możesz to zrobić za pomocą następującego kodu w konsoli:

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

Edytuj tę odpowiedź, jeśli wiesz, jak to zrobić w danych przeglądarkach lub innych przeglądarkach.


1

Właśnie się o tym dowiedziałem, próbując sprawdzić, czy moje wydarzenie było dołączone ....

Jeśli zrobisz :

item.onclick

zwróci „null”

ale jeśli to zrobisz:

item.hasOwnProperty('onclick')

to jest „PRAWDA”

więc myślę, że kiedy używasz „addEventListener” do dodawania programów obsługi zdarzeń, jedynym sposobem na dostęp do nich jest „hasOwnProperty”. Chciałbym wiedzieć, dlaczego i jak, ale niestety, po przeprowadzeniu badań, nie znalazłem wyjaśnienia.


2
onclickjest odrębny od .addEventListener- wpływa na atrybut, podczas gdy .addEventListenernie
— Zach Saucier

1

Jeśli dobrze rozumiem, możesz tylko sprawdzić, czy słuchacz został sprawdzony, ale nie, który słuchacz jest konkretnie prezenterem.

Więc jakiś kod ad hoc wypełni lukę w obsłudze przepływu kodowania. Praktyczną metodą byłoby utworzenie stateprzy użyciu zmiennych. Na przykład dołącz kontroler słuchacza w następujący sposób:

var listenerPresent=false

wtedy jeśli ustawisz odbiornik, po prostu zmień wartość:

listenerPresent=true

następnie wewnątrz wywołania zwrotnego eventListener możesz przypisać określone funkcje wewnątrz iw ten sam sposób rozdzielić dostęp do funkcjonalności w zależności od stanu jako zmiennej, na przykład:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

Po prostu usuń wydarzenie przed jego dodaniem:

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

To dobra sztuczka, ale zakładam, że działa tylko wtedy, gdy masz odniesienie do funkcji, która jest jednym z aktualnie „dodanych” detektorów zdarzeń. Myślę, że jest to ewidentna wada standardowego API, jeśli możemy dodać dowolnego nasłuchiwania zdarzeń, powinno być również możliwe sprawdzenie, jakie nasłuchiwania zdarzeń zostały do ​​tej pory dodane. To prawie tak, jakby nasłuchujący zdarzeń byli obecnie „zmiennymi tylko do zapisu”, co wydaje się mało znaną koncepcją.
— Panu Logic

0

Właśnie napisałem skrypt, który pozwala ci to osiągnąć. Daje ci dwie globalne funkcje: hasEvent(Node elm, String event)i getEvents(Node elm)którą możesz wykorzystać. Należy pamiętać, że modyfikuje EventTargetmetodę prototypową add/RemoveEventListeneri nie działa w przypadku zdarzeń dodanych za pomocą znaczników HTML lub składni javascriptelm.on_event = ...

Więcej informacji na GitHub

Live Demo

Scenariusz:

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

Teoretycznie można by zastosować piggyback addEventListener i removeEventListener, aby dodać, usunąć flagę z obiektu „this”. Brzydki i nie testowałem ...

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.