Javascript / DOM: Як видалити всі події об'єкта DOM?


97

Просто запитання: чи є спосіб повністю видалити всі події об’єкта, наприклад div?

EDIT: Я додаю за 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 14.03.17

Відповіді:


107

Я не впевнений, що ви маєте на увазі під час видалення всіх подій . Видалити всі обробники для певного типу події або всі обробники подій для одного типу?

Видаліть усі обробники подій

Якщо ви хочете видалити всі обробники подій (будь-якого типу), ви можете клонувати елемент і замінити його своїм клоном:

var clone = element.cloneNode(true);

Примітка: Це збереже атрибути та дочірні елементи, але не збереже жодних змін у властивостях DOM.


Видаліть "анонімні" обробники подій певного типу

Інший спосіб - використовувати, removeEventListener()але я думаю, ви вже спробували це, і це не спрацювало. Ось улов :

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

Ви по суті передаєте анонімну функцію addEventListenerяк 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коли ви їх знищуєте.


@Florian: Звичайно, код можна вдосконалити, але він повинен дати вам уявлення ... щасливого кодування!
Фелікс Клінг,

2
Ви пробували це з кількома елементами div? змінний вузол буде фактично перетворений у рядок, наприклад, "[об'єкт HTMLDivElement]", що означає, що в підсумку ви додасте все до того самого вузла.
cstruter

@cstruter: Правильно ... це було в мої перші дні JavaScript ... Я виправлю код, коли знайду трохи більше часу. Дякую що дали мені знати.
Фелікс Клінг,

Думаю, є помилка, addListener має бути addEvent
AGamePlayer

Зверніть увагу, що це element.parentNodeможе бути нулем. Напевно, слід поставити перевірку 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
Jay Dadhania

28

Використовуйте власну функцію прослуховувача подій remove(). Наприклад:

getEventListeners().click.forEach((e)=>{e.remove()})

2
Не впевнений, чому це неправильна відповідь. Питання з’являється стільки разів на SO, і відповіді можуть вирішити проблему методом клонування.
jimasun

42
Здається, getEventListenersце доступно лише в інструментах розробника WebKit та у FireBug. Не можна просто зателефонувати у свій код.
corwin.amber

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
Райан,

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 Велике спасибі.
Thavamani Kasi

5
Firefox каже мені, що getEventListenersне визначено?
rovyko

Це погане рішення, оскільки наразі 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, і change => to function, та різні інші речі.


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.


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

....


Звідки 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, але попередня залишається, тому у мене є попередня і одна, наприклад, нуль ...
Флоріан Мюллер

1
Не буде працювати, якщо обробники додаються через addEventListener().
Фелікс Клінг

@ Флоріан: Ні, це неправда. Якщо ви використовуєте цей метод додавання обробника події, у вас може бути лише один обробник події. Але це має значення, якщо ти використовуєш addEventListener()...
Фелікс Клінг

але я бачив це так, тому, якщо я додаю попередню функцію знову, вона буде викликана двічі ... і, як каже Фелікс Кінг, це не працює з addEventListener ...
Флоріан Мюллер

-1

Можливо, браузер зробить це за вас, якщо ви зробите щось на зразок:

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


зараз це насправді гарна ідея, але абсолютно непродуктивна, якщо вам доведеться це робити для цілої кількості divs від 1 000 до 1 000 000 ... Так, це великий проект;)
Флоріан Мюллер,

6
1 млн div на сторінці? Ви потрапите в інші неприємності до цього;) Може використовуватися делегування події тоді ...
Мікрофон

-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 в тесті транспортира і не маю доступу до слухача в тесті, наступне працює, щоб видалити всі прослуховувачі подій одного типу:

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

Я сподіваюся, це допоможе комусь, оскільки попередня відповідь використовувала метод "видалити", який з тих пір більше не працює.


-1

Одним із методів є додавання нового прослуховувача подій, який викликає e.stopImmediatePropagation ().

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.