Javascript / DOM: как удалить все события объекта DOM?

97

Просто вопрос: есть ли способ полностью удалить все события объекта, например, div?

РЕДАКТИРОВАТЬ: я добавляю для каждого div.addEventListener('click',eventReturner(),false);события.

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

EDIT2: я нашел способ, который работает, но не подходит для моего случая:

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);
}
Флориан Мюллер
источник
Как вы прикрепляете события?
Феликс Клинг
2
В заголовке задаются вопросы об элементах объекта, а в самом вопросе - о событиях . Вы хотите удалить дочерние элементы или события?
Pär Wieslander
о, черт возьми, это потому, что я думал о другом, когда писал это ... я забочусь о событиях
Флориан Мюллер
Я не знаю точного случая, но в некоторых случаях в качестве обходного пути вы можете использовать методы 'on *' (как div.onclick = function), которые всегда работают с одним слушателем и легко удаляются как div.onclick=null. Конечно, вы не должны использовать addEventListenerв этом случае вообще, так как это добавит отдельного слушателя, отличного от того, что находится в onclick.
Venimus

Ответы:

106

Я не уверен, что вы имеете в виду под удалением всех событий . Удалить все обработчики событий определенного типа или все обработчики событий одного типа?

Удалить все обработчики событий

Если вы хотите удалить все обработчики событий (любого типа), вы можете клонировать элемент и заменить его его клоном:

var clone = element.cloneNode(true);

Примечание. Это сохранит атрибуты и дочерние элементы, но не сохранит никаких изменений свойств DOM.


Удалить "анонимные" обработчики событий определенного типа.

Другой способ - использовать, removeEventListener()но я думаю, вы уже пробовали это, и это не сработало. Вот и загвоздка :

Вызов addEventListenerанонимной функции каждый раз создает нового слушателя. Вызов removeEventListenerанонимной функции не имеет никакого эффекта . Анонимная функция создает уникальный объект каждый раз при вызове, это не ссылка на существующий объект, хотя она может вызывать его. При добавлении прослушивателя событий таким образом убедитесь, что он добавляется только один раз, он постоянен (не может быть удален), пока объект, к которому он был добавлен, не будет уничтожен.

По сути, вы передаете анонимную функцию в addEventListeneras, eventReturnerвозвращающую функцию.

У вас есть две возможности решить эту проблему:

  1. Не используйте функцию, возвращающую функцию. Используйте функцию напрямую:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Создайте оболочку для addEventListenerхранения ссылки на возвращаемую функцию и создайте какую-нибудь странную removeAllEventsфункцию:

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

    И тогда вы можете использовать его с:

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

ДЕМО

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

Феликс Клинг
источник
@ Флориан: Конечно, код можно улучшить, но он должен дать вам представление ... удачного кодирования!
Феликс Клинг
2
Вы пробовали это с более чем одним элементом div? переменная узла фактически будет преобразована в строку, например, '[object HTMLDivElement]', что означает, что вы добавляете все в один и тот же узел.
cstruter
@cstruter: Верно ... это было на заре моей работы с JavaScript ... Я исправлю код, когда найду еще немного времени. Спасибо, что дали мне знать.
Феликс Клинг,
Я предполагаю, что это опечатка, addListener должен быть addEvent
AGamePlayer 01
Обратите внимание, что это element.parentNodeможет быть null. Вероятно, следует поставить проверку if вокруг этого фрагмента кода выше.
thecoolmacdude
43

Это удалит всех слушателей из детей, но будет медленным для больших страниц. Писать очень просто.

element.outerHTML = element.outerHTML;
пабомбы
источник
2
Отличный ответ. Для листовых узлов это кажется лучшей идеей.
user3055938
3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLработал у меня.
Райан
2
@Ryan вместо document.querySelector('body').outerHTMLвас можете просто ввестиdocument.body.outerHTML
Джей Дадхания
28

Используйте собственную функцию прослушивателя событий remove(). Например:

getEventListeners().click.forEach((e)=>{e.remove()})
JuanGG
источник
2
Не уверен, почему это неправильный ответ. Вопрос появляется так много раз в SO и может решить проблему с помощью метода клонирования.
jimasun
42
Похоже, getEventListenersэто доступно только в инструментах разработчика WebKit и в FireBug. Это не то, что вы можете просто вызвать в своем коде.
corwin.amber 01
1
не удалось заставить это работать, вы передаете объект в качестве аргумента getEventListeners(). Например: getEventListeners(document)илиgetEventListeners(document.querySelector('.someclass'))
Лука
10
Кажется, это работает только на хромированной консоли . Даже на вашем скрипте на странице это не работает.
Руэл Рибейро
4
getEventListeners(document).click.forEach((e)=>{e.remove()})выдает эту ошибку:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan
11

Как говорит corwin.amber, между Webkit и другими есть различия.

В Chrome:

getEventListeners(document);

Это дает вам объект со всеми существующими прослушивателями событий:

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

Отсюда вы можете добраться до слушателя, которого хотите удалить:

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

Итак, все слушатели событий:

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

В Firefox

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

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

Все слушатели событий:

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

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

Наслаждайтесь!

Jmakuc
источник
Это лучший ответ для нацеливания на удаление слушателей, например, в случае удаления определенного слушателя на узле документа без удаления всех его слушателей.
Trevor
Это работает нормально и лучший ответ. @Jmakuc Большое спасибо.
Thavamani Kasi
5
Firefox сообщает мне, что getEventListenersэто не определено?
Ровыко
Это плохое решение, потому что в настоящее время getEventListeners поддерживается только Chrome.
Майкл Паччоне
1

Вы можете добавить функцию перехвата для перехвата всех вызовов addEventHandler. Хук подтолкнет обработчик к списку, который можно использовать для очистки. Например,

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

Вы должны вставить этот код в верхнюю часть вашей главной веб-страницы (например index.html). Во время очистки вы можете пройти через all_handlers и вызвать removeEventHandler для каждого. Не беспокойтесь о многократном вызове removeEventHandler с одной и той же функцией. Это безвредно.

Например,

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

Примечание: для IE используйте Element вместо EventTarget, и измените => на функцию и многое другое.

Джон Хенкель
источник
0

Чтобы завершить ответы, вот реальные примеры удаления событий, когда вы посещаете веб-сайты и не можете контролировать сгенерированный код HTML и JavaScript.

Некоторые надоедливые веб-сайты мешают вам копировать и вставлять имена пользователей в формы входа, что можно было бы легко обойти, если бы onpasteсобытие было добавлено с onpaste="return false"атрибутом HTML. В этом случае нам просто нужно щелкнуть правой кнопкой мыши в поле ввода, выбрать «Проверить элемент» в браузере, таком как Firefox, и удалить атрибут HTML.

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

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

Нам также придется удалить событие через JavaScript:

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

В моем примере я использовал идентификатор lyca_login_mobile_no, поскольку это был идентификатор ввода текста, используемый веб-сайтом, который я посещал.

Другой способ удалить событие (которое также удалит все события) - удалить узел и создать новый, как мы должны сделать, если addEventListenerиспользовались для добавления событий с использованием анонимной функции, которую мы не можем удалить с помощьюremoveEventListener . Это также можно сделать через консоль браузера, проверив элемент, скопировав HTML-код, удалив HTML-код и затем вставив HTML-код в то же место.

Это также можно сделать быстрее и автоматизировать с помощью JavaScript:

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

Обновление: если веб-приложение создано с использованием инфраструктуры JavaScript, такой как Angular, похоже, что предыдущие решения не работают или нарушают работу приложения. Другой обходной путь для разрешения вставки - установка значения через JavaScript:

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

На данный момент я не знаю, есть ли способ удалить все события проверки и ограничения формы без нарушения работы приложения, полностью написанного на JavaScript, например Angular.

baptx
источник
0

У angular есть полифилл для этой проблемы, вы можете проверить. Я мало что понял, но, возможно, это поможет.

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
источник
Откуда EVENT_NAME_SYMBOL_REGXвзяться?
Элиас Замария,
0

Вы можете добавить вспомогательную функцию, которая, например, очищает прослушиватель событий.

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

просто передайте элемент в функцию и все ...

Абхилаш Нараян
источник
-1
var div = getElementsByTagName('div')[0]; /* first div found; you can use getElementById for more specific element */
div.onclick = null; // OR:
div.onclick = function(){};

//редактировать

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

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

подробнее подробнее

Ионуй Стайку
источник
1
если я установил это так, div.onClick = somethingтогда он установит другое событие onClick, но предыдущее останется, поэтому у меня есть предыдущее и одно, например, null ...
Флориан Мюллер
1
Не будет работать, если обработчики добавлены через addEventListener().
Феликс Клинг
@ Флориан: Нет, это неправда. Если вы используете этот метод добавления обработчика событий, у вас может быть только один обработчик для события. Но есть разница, если вы используете addEventListener()...
Феликс Клинг
но я видел это таким, поэтому, если я снова добавлю предыдущую функцию, она будет вызываться дважды ... и, как говорит Феликс Кинг, это не работает с addEventListener ...
Флориан Мюллер,
-1

Может быть, браузер сделает это за вас, если вы сделаете что-то вроде:

Скопируйте divи его атрибуты и вставьте перед старым, затем переместите содержимое из старого в новое и удалите старое?

Микрофон
источник
теперь это на самом деле хорошая идея, но совершенно неэффективная, если вам нужно сделать это для множества div от 1'000 до 1'000'000 ... Да, это большой проект;)
Флориан Мюллер
6
1M div на странице? Перед этим вы столкнетесь с другими проблемами;) Тогда можно использовать делегирование событий ...
Mic
-1

Удаление всех событий наdocument :

Один лайнер:

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

Симпатичная версия:

for (key in getEventListeners(document)) {
  getEventListeners(document)[key].forEach(function(c) {
    c.remove()
  })   
}
Дориан
источник
-1

Только Chrome

Поскольку я пытаюсь удалить EventListener в тесте Protractor и не имею доступа к Listener в тесте, для удаления всех прослушивателей событий одного типа работает следующее:

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

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

Филипе Мело
источник
-1

Один из способов - добавить новый прослушиватель событий, который вызывает e.stopImmediatePropagation ().

Лачо Томов
источник