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?

Stano
źródło
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).

Ivan
źródło
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.

Karol Grabowski
źródło
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);
 }
}
Cesar
źródło
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. 😊

Anioł Politis
źródło
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.

Paul Leclerc
źródło
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;
};
1,21 gigawata
źródło
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');
}
dsmith63
źródło
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.

Dave F.
źródło
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.

carinlynchin
źródło
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
Webwoman
źródło
1

Po prostu usuń wydarzenie przed jego dodaniem:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
Entretoize
źródło
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)}();
Gaurang Tandon
źródło
-1

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

kofifus
źródło