最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - remove event listener without knowing what the call back function is - Stack Overflow

programmeradmin2浏览0评论

Lets say we add a even listener to a button like this

function handler(){
 alert(0)
}
btn.addEventListener("click", handler)

now if we want to remove event listener we can simply do: btn.removeEventListener("click", handler)

but what if we don't have access to the handler function. for example, some third-party library is attaching an event listener to an element and I want to remove it. how can we do so?

Lets say we add a even listener to a button like this

function handler(){
 alert(0)
}
btn.addEventListener("click", handler)

now if we want to remove event listener we can simply do: btn.removeEventListener("click", handler)

but what if we don't have access to the handler function. for example, some third-party library is attaching an event listener to an element and I want to remove it. how can we do so?

Share Improve this question asked Aug 26, 2021 at 12:42 Md ShuvoMd Shuvo 693 silver badges9 bronze badges 7
  • 2 You cannot remove it without the handler/original function reference. – evolutionxbox Commented Aug 26, 2021 at 12:43
  • 3 Only way to remove event listeners is to recreate the element – epascarello Commented Aug 26, 2021 at 12:45
  • 2 Probably you should look into third-party library documentation and see if there is any remove event. – ikhvjs Commented Aug 26, 2021 at 12:45
  • 1 btn.replaceWith(btn.cloneNode(true)); sure there are duplicates with that answer – epascarello Commented Aug 26, 2021 at 12:49
  • @epascarello just answered with a possible alternative that will not remove the event listeners, but should prevent third-party listeners from firing. – Ernesto Stifano Commented Aug 26, 2021 at 13:27
 |  Show 2 more ments

3 Answers 3

Reset to default 5

As written in ments, you can't remove an event listener without having the callback reference unless you re-create/clone that element, which can be problematic sometimes and also does not seem to be good practice or particularly performant (Spoiler Alert - None of the possible solutions can be pletely considered good practice because your situation isn't ideal from the beginning).

As an alternative, you can try something like this:

element.addEventListener('click', (e) => {
  e.stopImmediatePropagation();
  e.stopPropagation();
}, true);

Odds are that your third-party library will be listening to the event on the bubbling phase. If so, doing this on the capture phase (see addEventListener third argument) will prevent your third-party listeners from firing.

Please note that this will break event propagation at the capture phase. This means that any listener set on the bubbling phase for that event, triggered on that element or any of its children will not fire. Also the remaining listeners on the capture phase will not fire either.

Leveraging Event's Nature

If you add the above event listener before your third-party library adds its own event listeners, you can be "more sure" that yours will be fired first.

Please consider that capture phase listeners will ALWAYS be fired before bubbling phase listeners and that element's DOM order will ALWAYS be respected.

What is a bit unpredictable is the execution order for multiple listeners for the same event on the same phase and on the same target element. See Event Order.

With that being said, if you add the above listener to the PARENT of your interested element, you can be 99% sure that your third-party library listener will not be executed and this es with almost zero performance impact. (The remaining 1% is for the case in which the library somehow adds an event listener on the capture phase and on a previous ancestor than yours, that could also be the window object).

If you want to still fire listeners for the parent element you can filter out cases using event.target property.

Something like:

parent.addEventListener('click', (e) => {
  if (element.contains(e.target)) {
    e.stopPropagation();
  }
}, true);

Final Note

I took the time to write all this more as a didactic example about DOM events than as an actual working solution. This is because any possible solution that I can think about right now will have some kind of drawback, issue or limitation (performance, debugging, unpredictability, etc.)

This is what I tried(It didnt work tho)

function removeEListener(element, event){
    let listeners = getEventListeners(element)[event]
    try{
        listeners.forEach(listener => {
            listener.remove()
            element.removeEventListener(event, listener.listener, listener.useCapture)
        })
    }catch(e){
        console.log(e)
    }
}
removeEListener(document.querySelector(".btn"), 'click')

A simpler way is to clone node and its child elements to remove all the listeners.

function recreateNode(el, withChildren) {
  if (withChildren) {
    el.parentNode.replaceChild(el.cloneNode(true), el);
  }
  else {
    var newEl = el.cloneNode(false);
    while (el.hasChildNodes()) newEl.appendChild(el.firstChild);
    el.parentNode.replaceChild(newEl, el);
  }
}

Here withChildren: true will replace all nested elements as well. With your question, You don't need to find out the reference function.

发布评论

评论列表(0)

  1. 暂无评论