Javascript / DOM: Jak usunąć wszystkie zdarzenia z obiektu DOM?

99

Tylko pytanie: czy istnieje sposób, aby całkowicie usunąć wszystkie zdarzenia obiektu, np. Div?

EDYCJA: dodaję na div.addEventListener('click',eventReturner(),false);wydarzenie.

function eventReturner() {
    return function() {
        dosomething();
    };
}

EDIT2: znalazłem sposób, który działa, ale nie można go użyć w moim przypadku:

var returnedFunction;
function addit() {
    var div = document.getElementById('div');
    returnedFunction = eventReturner();
    div.addEventListener('click',returnedFunction,false); //You HAVE to take here a var and not the direct call to eventReturner(), because the function address must be the same, and it would change, if the function was called again.
}
function removeit() {
    var div = document.getElementById('div');
    div.removeEventListener('click',returnedFunction,false);
}
Florian Müller
źródło
Jak dołączasz wydarzenia?
Felix Kling
2
Tytuł pyta o elementy obiektu, a samo pytanie o zdarzenia . Czy chcesz usunąć elementy podrzędne lub zdarzenia?
Pär Wieslander
oh d * mn, to dlatego, że myślałem o czymś innym, kiedy napisałem, że ... dbam o wydarzenia
Florian Müller
Nie znam dokładnego przypadku, ale w niektórych przypadkach jako obejście można użyć metod 'on *' (jak div.onclick = function), które zawsze działają z jednym odbiornikiem i można je łatwo usunąć jako div.onclick=null. Oczywiście nie powinieneś używać addEventListenerw tym przypadku całkowicie, ponieważ doda to oddzielny słuchacz inny niż w onclick.
venimus

Odpowiedzi:

108

Nie jestem pewien, co masz na myśli, mówiąc o usunięciu wszystkich wydarzeń . Usunąć wszystkie programy obsługi dla określonego typu zdarzenia lub wszystkie programy obsługi zdarzeń dla jednego typu?

Usuń wszystkie programy obsługi zdarzeń

Jeśli chcesz usunąć wszystkie programy obsługi zdarzeń (dowolnego typu), możesz sklonować element i zastąpić go jego klonem:

var clone = element.cloneNode(true);

Uwaga: zachowa to atrybuty i elementy podrzędne, ale nie zachowa żadnych zmian we właściwościach DOM.


Usuń „anonimowe” programy obsługi zdarzeń określonego typu

Innym sposobem jest użycie, removeEventListener()ale myślę, że już próbowałeś tego i nie zadziałało. Oto haczyk :

Wywołanie addEventListeneranonimowej funkcji za każdym razem tworzy nowego słuchacza. Wywołanie removeEventListenerfunkcji anonimowej nie ma żadnego efektu . Funkcja anonimowa tworzy unikalny obiekt za każdym razem, gdy jest wywoływana, nie jest odniesieniem do istniejącego obiektu, chociaż może go wywołać. Dodając detektor zdarzeń w ten sposób, upewnij się, że został dodany tylko raz, jest trwały (nie można go usunąć), dopóki obiekt, do którego został dodany, nie zostanie zniszczony.

Zasadniczo przekazujesz anonimową funkcję do addEventListeneras eventReturnerzwraca funkcję.

Masz dwie możliwości rozwiązania tego problemu:

  1. Nie używaj funkcji, która zwraca funkcję. Użyj funkcji bezpośrednio:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Utwórz opakowanie addEventListenerprzechowujące odniesienie do zwróconej funkcji i utwórz dziwną removeAllEventsfunkcję:

    var _eventHandlers = {}; // somewhere global
    
    const addListener = (node, event, handler, capture = false) => {
      if (!(event in _eventHandlers)) {
        _eventHandlers[event] = []
      }
      // here we track the events and their nodes (note that we cannot
      // use node as Object keys, as they'd get coerced into a string
      _eventHandlers[event].push({ node: node, handler: handler, capture: capture })
      node.addEventListener(event, handler, capture)
    }
    
    const removeAllListeners = (targetNode, event) => {
      // remove listeners from the matching nodes
      _eventHandlers[event]
        .filter(({ node }) => node === targetNode)
        .forEach(({ node, handler, capture }) => node.removeEventListener(event, handler, capture))
    
      // update _eventHandlers global
      _eventHandlers[event] = _eventHandlers[event].filter(
        ({ node }) => node !== targetNode,
      )
    }
    

    A potem możesz go użyć z:

    addListener(div, 'click', eventReturner(), false)
    // and later
    removeAllListeners(div, 'click')
    

PRÓBNY

Uwaga: Jeśli twój kod działa przez długi czas i tworzysz i usuwasz wiele elementów, musiałbyś upewnić się, że usuniesz elementy zawarte w, _eventHandlersgdy je zniszczysz.

Felix Kling
źródło
@Florian: Oczywiście kod można ulepszyć, ale powinien dać ci pomysł ... szczęśliwego kodowania!
Felix Kling
2
Czy próbowałeś tego z więcej niż jednym elementem div? węzeł var zostanie faktycznie przekonwertowany na łańcuch, np. „[obiekt HTMLDivElement]”, co oznacza, że ​​w końcu dodasz wszystko do tego samego węzła.
cstruter
@cstruter: Racja ... to było we wczesnych latach mojej pracy z JavaScriptem ... Poprawię kod, gdy znajdę więcej czasu. Dzięki, że dałeś mi znać.
Felix Kling
Myślę, że jest literówka, addListener powinno być addEvent
AGamePlayer
Zauważ, że element.parentNodemoże to być null. Powinien prawdopodobnie umieścić if check wokół tego fragmentu kodu powyżej.
thecoolmacdude
46

Spowoduje to usunięcie wszystkich słuchaczy z dzieci, ale będzie powolne w przypadku dużych stron. Brutalnie proste do napisania.

element.outerHTML = element.outerHTML;
pabombs
źródło
2
Świetna odpowiedź. W przypadku węzłów liści wydaje się to najlepszym pomysłem.
user3055938
3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLpracował dla mnie.
Ryan
3
@Ryan zamiast document.querySelector('body').outerHTMLciebie możesz po prostu wpisaćdocument.body.outerHTML
Jay Dadhania
29

Użyj własnej funkcji detektora zdarzeń remove(). Na przykład:

getEventListeners().click.forEach((e)=>{e.remove()})
JuanGG
źródło
2
Nie wiem, dlaczego to nie jest właściwa odpowiedź. Pytanie pojawia się tak wiele razy w SO i odpowiedzi mogą rozwiązać problem przy użyciu metody klonowania.
jimasun
45
Wygląda na getEventListenersto, że jest dostępny tylko w narzędziach deweloperskich WebKit i FireBug. Nie jest to coś, co możesz po prostu wpisać w swoim kodzie.
corwin.amber
1
miał problem z uruchomieniem tego, przekazujesz obiekt jako argument do getEventListeners(). przykład: getEventListeners(document)lubgetEventListeners(document.querySelector('.someclass'))
Łukasz
10
Wygląda na to, że działa tylko na konsoli Chrome . Nawet na twoim skrypcie na stronie to nie działa.
Reuel Ribeiro
4
getEventListeners(document).click.forEach((e)=>{e.remove()})generuje ten błąd:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan
12

Jak mówi corwin.amber, istnieją różnice między Webkitem a innymi.

W przeglądarce Chrome:

getEventListeners(document);

Co daje Ci obiekt ze wszystkimi istniejącymi odbiornikami zdarzeń:

Object 
 click: Array[1]
 closePopups: Array[1]
 keyup: Array[1]
 mouseout: Array[1]
 mouseover: Array[1]
 ...

Z tego miejsca możesz dotrzeć do słuchacza, którego chcesz usunąć:

getEventListeners(document).copy[0].remove();

Więc wszyscy słuchacze wydarzenia:

for(var eventType in getEventListeners(document)) {
   getEventListeners(document)[eventType].forEach(
      function(o) { o.remove(); }
   ) 
}

W przeglądarce Firefox

Jest trochę inny, ponieważ używa opakowania nasłuchującego, które nie zawiera funkcji usuwania. Musisz zdobyć odbiorcę, którego chcesz usunąć:

document.removeEventListener("copy", getEventListeners(document).copy[0].listener)

Wszyscy słuchacze wydarzeń:

for(var eventType in getEventListeners(document)) {
  getEventListeners(document)[eventType].forEach(
    function(o) { document.removeEventListener(eventType, o.listener) }
  ) 
}

Natknąłem się na ten post, próbując wyłączyć irytującą ochronę przed kopiowaniem witryny z wiadomościami.

Cieszyć się!

Jmakuc
źródło
Jest to najlepsza odpowiedź, jeśli chcesz usunąć detektory, na przykład w przypadku usunięcia określonego odbiornika w węźle dokumentu bez usuwania wszystkich jego detektorów.
Trevor
To działa dobrze i najlepsza odpowiedź. @Jmakuc Wielkie dzięki.
Thavamani Kasi,
5
Firefox mówi mi, że getEventListenersnie jest zdefiniowane?
rovyko
To kiepskie rozwiązanie, ponieważ obecnie getEventListeners jest obsługiwane tylko przez Chrome.
Michael Paccione
2

Możesz dodać funkcję widełek do przechwytywania wszystkich połączeń addEventHandler. Hak spowoduje wypchnięcie programu obsługi do listy, której można użyć do czyszczenia. Na przykład,

if (EventTarget.prototype.original_addEventListener == null) {
    EventTarget.prototype.original_addEventListener = EventTarget.prototype.addEventListener;

    function addEventListener_hook(typ, fn, opt) {
        console.log('--- add event listener',this.nodeName,typ);
        this.all_handlers = this.all_handlers || [];
        this.all_handlers.push({typ,fn,opt});
        this.original_addEventListener(typ, fn, opt);  
    }

    EventTarget.prototype.addEventListener = addEventListener_hook;
}

Powinieneś wstawić ten kod u góry głównej strony internetowej (np index.html.). Podczas czyszczenia możesz zapętlić all_handlers i wywołać metodę removeEventHandler dla każdego z nich. Nie martw się o wielokrotne wywoływanie metody removeEventHandler z tą samą funkcją. To jest nieszkodliwe.

Na przykład,

function cleanup(elem) {
    for (let t in elem) if (t.startsWith('on') && elem[t] != null) {
        elem[t] = null;
        console.log('cleanup removed listener from '+elem.nodeName,t);
    } 
    for (let t of elem.all_handlers || []) {
        elem.removeEventListener(t.typ, t.fn, t.opt);
        console.log('cleanup removed listener from '+elem.nodeName,t.typ);
    } 
}

Uwaga: w przypadku IE użyj Element zamiast EventTarget i zmień => na funkcję i różne inne rzeczy.

John Henckel
źródło
1

Aby uzupełnić odpowiedzi, oto rzeczywiste przykłady usuwania zdarzeń, gdy odwiedzasz strony internetowe i nie masz kontroli nad generowanym kodem HTML i JavaScript.

Niektóre irytujące witryny uniemożliwiają kopiowanie i wklejanie nazw użytkowników w formularzach logowania, co można by łatwo ominąć, gdyby onpastezdarzenie zostało dodane z onpaste="return false"atrybutem HTML. W tym przypadku wystarczy kliknąć prawym przyciskiem myszy pole wejściowe, wybrać opcję „Sprawdź element” w przeglądarce takiej jak Firefox i usunąć atrybut HTML.

Jeśli jednak zdarzenie zostało dodane za pomocą JavaScript w następujący sposób:

document.getElementById("lyca_login_mobile_no").onpaste = function(){return false};

Będziemy musieli również usunąć zdarzenie za pomocą JavaScript:

document.getElementById("lyca_login_mobile_no").onpaste = null;

W moim przykładzie użyłem identyfikatora „lyca_login_mobile_no”, ponieważ był to identyfikator wprowadzania tekstu używany przez odwiedzaną witrynę internetową.

Innym sposobem na usunięcie zdarzenia (który również usunie wszystkie zdarzenia) jest usunięcie węzła i utworzenie nowego, tak jak musimy to zrobić, jeśli addEventListenerzostał użyty do dodania zdarzeń przy użyciu funkcji anonimowej, której nie możemy usunąć removeEventListener. Można to również zrobić za pomocą konsoli przeglądarki, sprawdzając element, kopiując kod HTML, usuwając kod HTML, a następnie wklejając kod HTML w tym samym miejscu.

Można to również zrobić szybciej i zautomatyzować za pomocą JavaScript:

var oldNode = document.getElementById("lyca_login_mobile_no");
var newNode = oldNode.cloneNode(true);
oldNode.parentNode.insertBefore(newNode, oldNode);
oldNode.parentNode.removeChild(oldNode);

Aktualizacja: jeśli aplikacja internetowa została utworzona przy użyciu struktury JavaScript, takiej jak Angular, wygląda na to, że poprzednie rozwiązania nie działają lub nie psują aplikacji. Innym obejściem umożliwiającym wklejanie byłoby ustawienie wartości za pomocą JavaScript:

document.getElementById("lyca_login_mobile_no").value = "username";

W tej chwili nie wiem, czy istnieje sposób na usunięcie wszystkich zdarzeń walidacji formularzy i ograniczeń bez niszczenia aplikacji napisanej w całości w JavaScript, takiej jak Angular.

baptx
źródło
1

możesz dodać funkcję i usunąć wszystkie inne kliknięcia, przypisując je

btn1 = document.querySelector(".btn-1")
btn1.addEventListener("click" , _=>{console.log("hello")})
btn1.addEventListener("click" , _=>{console.log("How Are you ?")})

btn2 = document.querySelector(".btn-2")
btn2.onclick = _=>{console.log("Hello")}
btn2.onclick = _=>{console.log("Bye")}
<button class="btn-1">Hello to Me</button>
<button class="btn-2">Hello to Bye</button>

hossein sedighian
źródło
0

angular ma wypełnienie dla tego problemu, możesz sprawdzić. Nie rozumiałem wiele, ale może to pomoże.

const REMOVE_ALL_LISTENERS_EVENT_LISTENER = 'removeAllListeners';

    proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function () {
        const target = this || _global;
        const eventName = arguments[0];
        if (!eventName) {
            const keys = Object.keys(target);
            for (let i = 0; i < keys.length; i++) {
                const prop = keys[i];
                const match = EVENT_NAME_SYMBOL_REGX.exec(prop);
                let evtName = match && match[1];
                // in nodejs EventEmitter, removeListener event is
                // used for monitoring the removeListener call,
                // so just keep removeListener eventListener until
                // all other eventListeners are removed
                if (evtName && evtName !== 'removeListener') {
                    this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName);
                }
            }
            // remove removeListener listener finally
            this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener');
        }
        else {
            const symbolEventNames = zoneSymbolEventNames$1[eventName];
            if (symbolEventNames) {
                const symbolEventName = symbolEventNames[FALSE_STR];
                const symbolCaptureEventName = symbolEventNames[TRUE_STR];
                const tasks = target[symbolEventName];
                const captureTasks = target[symbolCaptureEventName];
                if (tasks) {
                    const removeTasks = tasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
                if (captureTasks) {
                    const removeTasks = captureTasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
            }
        }
        if (returnTarget) {
            return this;
        }
    };

....

feyzullahyildiz
źródło
Skąd EVENT_NAME_SYMBOL_REGXma pochodzić?
Elias Zamaria,
0

Możesz dodać funkcję pomocniczą, która na przykład czyści nasłuchiwanie zdarzeń

function clearEventListener(element) {
 const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}

wystarczy przekazać element do funkcji i to wszystko ...

Abhilash Narayan
źródło
-1
var div = getElementsByTagName('div')[0]; /* first div found; you can use getElementById for more specific element */
div.onclick = null; // OR:
div.onclick = function(){};

//edytować

Nie wiedziałem, jakiej metody używasz do dołączania wydarzeń. Dla addEventListenermożna użyć to:

div.removeEventListener('click',functionName,false); // functionName is the name of your callback function

więcej szczegółów

Ionuț Staicu
źródło
1
jeśli ustawię to tak, div.onClick = somethingto ustawia inne zdarzenie onClick, ale poprzednie pozostaje, więc mam na poprzednie i jedno na przykład null ...
Florian Müller
1
Nie będzie działać, jeśli programy obsługi zostaną dodane za pośrednictwem addEventListener().
Felix Kling
@Florian: Nie, to nieprawda. Jeśli używasz tej metody dodawania programu obsługi zdarzeń, możesz mieć tylko jedną procedurę obsługi zdarzenia. Ale robi różnicę, jeśli używasz addEventListener()...
Felix Kling
ale widziałem to w ten sposób, więc jeśli ponownie dodam poprzednią funkcję, zostanie ona wywołana dwukrotnie ... i, jak mówi Felix King, nie działa z addEventListener ...
Florian Müller
-1

Być może przeglądarka zrobi to za Ciebie, jeśli zrobisz coś takiego:

Skopiować divi jego atrybuty i wstawić przed starą, a następnie przenieść zawartość ze starej do nowej i usunąć starą?

Mic
źródło
to jest właściwie dobry pomysł, ale całkowicie nieskuteczny, jeśli musisz to zrobić dla mnóstwa div od 1'000 do 1'000'000 ... Tak, to duży projekt;)
Florian Müller
6
1 mln elementów div na stronie? Będziesz miał inne kłopoty przed tym;) Możesz użyć delegacji wydarzenia, a następnie ...
Mic
-1

Usuwanie wszystkich wydarzeń zdocument :

Jedna wkładka:

for (key in getEventListeners(document)) { getEventListeners(document)[key].forEach(function(c) { c.remove() }) }

Ładna wersja:

for (key in getEventListeners(document)) {
  getEventListeners(document)[key].forEach(function(c) {
    c.remove()
  })   
}
dorycki
źródło
-1

Tylko Chrome

Ponieważ próbuję usunąć EventListener w teście Protractor i nie mam dostępu do Listenera w teście, poniższe działa, aby usunąć wszystkie detektory zdarzeń jednego typu:

function(eventType){
    getEventListeners(window).[eventType].forEach(
        function(e){
            window.removeEventListener(eventType, e.listener)
        }
    );
}

Mam nadzieję, że komuś to pomogło, ponieważ poprzednia odpowiedź używała metody „usuń”, która od tego czasu już nie działa.

Filipe Melo
źródło
-1

Jedną z metod jest dodanie nowego detektora zdarzeń, który wywołuje funkcję e.stopImmediatePropagation ().

Tomov
źródło