【问题标题】:Remove All Event Listeners of Specific Type删除特定类型的所有事件侦听器
【发布时间】:2013-10-28 11:46:41
【问题描述】:

我想删除所有使用addEventListener() 添加的特定类型的事件侦听器。我看到的所有资源都在说你需要这样做:

elem.addEventListener('mousedown',specific_function);
elem.removeEventListener('mousedown',specific_function);

但我希望能够在不知道它当前是什么的情况下清除它,就像这样:

elem.addEventListener('mousedown',specific_function);
elem.removeEventListener('mousedown');

【问题讨论】:

    标签: javascript events


    【解决方案1】:

    如果不拦截 addEventListener 调用并跟踪侦听器或使用允许此类功能的库,这是不可能的。如果可以访问 listeners 集合,但 feature wasn't implemented 会是这样。

    您可以做的最接近的事情是通过克隆元素来删除所有侦听器,这不会克隆侦听器集合。

    注意:这也会移除元素子元素的监听器。

    var el = document.getElementById('el-id'),
        elClone = el.cloneNode(true);
    
    el.parentNode.replaceChild(elClone, el);
    

    【讨论】:

    • 我认为您假设替换的节点(带有事件侦听器)将被垃圾收集。如果不是这样,您可能会遇到奇怪的问题。
    • @Reno 孤立元素及其监听器应该在所有现代浏览器中进行垃圾回收。显然,如果你在 JS 的某个地方持有一些对初始 DOM 节点的引用,你将不得不考虑到这一点。
    • @Hector, window 不是 DOM 元素,所以它不会。
    • 另一个不好的地方是它会破坏对这个节点的引用。
    • 这会删除所有听众,而不仅仅是特定类型的听众,因此从技术上讲,这不是这个问题的公认答案。除非你明确声明这是不可能的??!!
    【解决方案2】:

    如果您移除监听器的唯一目的是阻止它们运行,您可以在窗口中添加一个事件监听器来捕获并取消给定类型的所有事件:

    window.addEventListener(type, function(event) {
        event.stopImmediatePropagation();
    }, true);
    

    为第三个参数传入true 会导致事件在下降过程中被捕获。停止传播意味着事件永远不会到达正在侦听它的侦听器。

    请记住,尽管它的用途非常有限,因为您无法为给定类型添加新的侦听器(它们都将被阻止)。有一些方法可以解决这个问题,例如,通过触发一种只有你的听众才知道要听的新事件。您可以这样做:

    window.addEventListener('click', function (event) {
        // (note: not cross-browser)
        var event2 = new CustomEvent('click2', {detail: {original: event}});
        event.target.dispatchEvent(event2);
        event.stopPropagation();
    }, true);
    
    element.addEventListener('click2', function(event) {
        if (event.detail && event.detail.original) {
            event = event.detail.original
        }
        // Do something with event
    });
    

    但是,请注意,这可能不适用于 mousemove 等快速事件,因为重新调度事件会引入延迟。

    如果您需要这样做,最好只跟踪最初添加的听众,如 Martin Wantke 的回答中所述。

    【讨论】:

    • 如果事件绑定到窗口对象怎么办,比如在消息事件上?
    【解决方案3】:

    您必须重写 EventTarget.prototype.addEventListener 以构建一个陷阱函数来记录所有“添加侦听器”调用。像这样的:

    var _listeners = [];
    
    EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener)
    {
        _listeners.push({target: this, type: type, listener: listener});
        this.addEventListenerBase(type, listener);
    };
    

    然后你可以构建一个EventTarget.prototype.removeEventListeners

    EventTarget.prototype.removeEventListeners = function(targetType)
    {
        for(var index = 0; index != _listeners.length; index++)
        {
            var item = _listeners[index];
    
            var target = item.target;
            var type = item.type;
            var listener = item.listener;
    
            if(target == this && type == targetType)
            {
                this.removeEventListener(type, listener);
            }
        }
    }
    

    在 ES6 中,你可以使用 Symbol,在实例化对象自身中直接隐藏原始函数和所有添加的侦听器列表。

    (function()
    {
        let target = EventTarget.prototype;
        let functionName = 'addEventListener';
        let func = target[functionName];
    
        let symbolHidden = Symbol('hidden');
    
        function hidden(instance)
        {
            if(instance[symbolHidden] === undefined)
            {
                let area = {};
                instance[symbolHidden] = area;
                return area;
            }
    
            return instance[symbolHidden];
        }
    
        function listenersFrom(instance)
        {
            let area = hidden(instance);
            if(!area.listeners) { area.listeners = []; }
            return area.listeners;
        }
    
        target[functionName] = function(type, listener)
        {
            let listeners = listenersFrom(this);
    
            listeners.push({ type, listener });
    
            func.apply(this, [type, listener]);
        };
    
        target['removeEventListeners'] = function(targetType)
        {
            let self = this;
    
            let listeners = listenersFrom(this);
            let removed = [];
    
            listeners.forEach(item =>
            {
                let type = item.type;
                let listener = item.listener;
    
                if(type == targetType)
                {
                    self.removeEventListener(type, listener);
                }
            });
        };
    })();
    

    你可以用这个小狙击手测试这段代码:

    document.addEventListener("DOMContentLoaded", event => { console.log('event 1'); });
    document.addEventListener("DOMContentLoaded", event => { console.log('event 2'); });
    document.addEventListener("click", event => { console.log('click event'); });
    
    document.dispatchEvent(new Event('DOMContentLoaded'));
    document.removeEventListeners('DOMContentLoaded');
    document.dispatchEvent(new Event('DOMContentLoaded'));
    // click event still works, just do a click in the browser
    

    【讨论】:

      【解决方案4】:

      删除全局事件的所有监听器

      element.onmousedown = null;
      

      现在您可以通过

      返回添加事件侦听器
      element.addEventListener('mousedown', handler, ...);
      

      此解决方案仅适用于“全局”事件。自定义事件不起作用。以下是所有全球事件的列表:https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers

      【讨论】:

        【解决方案5】:

        我知道这是旧的,但我有一个类似的问题,没有真正的答案,我想从文档中删除所有 keydown 事件侦听器。我没有删除它们,而是覆盖addEventListener 以在它们被添加之前忽略它们,类似于上面的汤姆斯回答,通过在加载任何其他脚本之前添加它:

        <script type="text/javascript">
            var current = document.addEventListener;
            document.addEventListener = function (type, listener) {
                if(type =="keydown")
                {
                    //do nothing
                }
                else
                {
                    var args = [];
                    args[0] = type;
                    args[1] = listener;
                    current.apply(this, args);
                }
            };
        </script>
        

        【讨论】:

        • 您可以直接传递argumentstype === 'keydown' || current.apply(this, arguments); 将是一个很酷的单行。您还可以将整个内容包装在 IIFE 中,以防止 current 泄漏到全局范围内。
        【解决方案6】:

        所以这个函数去掉了元素上大部分指定的监听器类型:

        function removeListenersFromElement(element, listenerType){
          const listeners = getEventListeners(element)[listenerType];
          let l = listeners.length;
          for(let i = l-1; i >=0; i--){
            removeEventListener(listenerType, listeners[i].listener);
          }
         }
        

        有一些罕见的例外情况,由于某种原因无法删除。

        【讨论】:

        • 迄今为止最好的答案!为什么之前没有人提到getEventListeners
        • 很抱歉,但我必须收回我所说的,getEventListeners 仅在命令行中的 ChromeDevTools 中工作,因此几乎在任何情况下都没有用。 codepen.io/azaslavsky/pres/sybfE
        【解决方案7】:

        通过一行js删除元素中的所有监听器:

        element.parentNode.innerHTML += '';
        

        【讨论】:

        • 只有在 element 是唯一的孩子的情况下才有效。
        • 还有element.outerHTML = element.outerHTML
        【解决方案8】:

        在不知道哪个回调附加到窗口侦听器的极端情况下,处理程序可以包装窗口 addEventListener 并且变量可以存储任何侦听器以通过 removeAllEventListener('scroll') 正确删除每个侦听器.

        var listeners = {};
        
        var originalEventListener = window.addEventListener;
        window.addEventListener = function(type, fn, options) {
            if (!listeners[type])
                listeners[type] = [];
        
            listeners[type].push(fn);
            return originalEventListener(type, fn, options);
        }
        
        var removeAllEventListener = function(type) {
            if (!listeners[type] || !listeners[type].length)
                return;
        
            for (let i = 0; i < listeners[type].length; i++)
                window.removeEventListener(type, listeners[type][i]);
        }
        

        【讨论】:

          【解决方案9】:

          您不能删除单个事件,但是全部?立刻?做吧

          document.body.innerHTML = document.body.innerHTML

          【讨论】:

          • 我不明白为什么这被否决了,这里最简单的一个
          • 因为这完全是矫枉过正,这样做会重置整个页面,并可能会产生所有可能的副作用
          • @FlavienVolken 也许如果您了解 DOM 是如何工作的,以及为什么是正确的答案,您就不会认为它是矫枉过正的。这不是矫枉过正,因为它是唯一的方法,除非你有更好的方法来做到这一点......是吗?
          • 我一直对不懂技术的人评论优化感到困惑......您如何优化您不了解的技术/框架? @FlavienVolken
          • 问题是“删除所有特定类型的事件监听器”,您的解决方案将删除任何类型页面的所有监听器。
          【解决方案10】:

          您也可以覆盖 'yourElement.addEventListener()' 方法并使用 '.apply()' 方法像平常一样执行侦听器,但在进程中拦截函数。喜欢:

          <script type="text/javascript">
          
              var args = [];
              var orginalAddEvent = yourElement.addEventListener;
          
              yourElement.addEventListener = function() {
                  //console.log(arguments);
                  args[args.length] = arguments[0];
                  args[args.length] = arguments[1];
                  orginalAddEvent.apply(this, arguments);
              };
          
              function removeListeners() {
                  for(var n=0;n<args.length;n+=2) {
                      yourElement.removeEventListener(args[n], args[n+1]);
                  }
              }
          
              removeListeners();
          
          </script>
          

          此脚本必须在页面加载时运行,否则它可能不会拦截所有事件侦听器。

          确保在使用前删除“removeListeners()”调用。

          【讨论】:

            【解决方案11】:

            在不引用原始函数的情况下删除事件侦听器的现代方法是使用AbortController。需要注意的是,您只能中止自己添加的侦听器。

            const buttonOne = document.querySelector('#button-one');
            const buttonTwo = document.querySelector('#button-two');
            const abortController = new AbortController();
            
            // Add multiple click event listeners to button one
            buttonOne.addEventListener(
              'click',
              () => alert('First'),
              { signal: abortController.signal }
            );
            
            buttonOne.addEventListener(
              'click',
              () => alert('Second'),
              { signal: abortController.signal }
            );
            
            // Add listener to remove first button's listeners
            buttonTwo.addEventListener(
              'click',
              () => abortController.abort()
            );
            <p>The first button will fire two alert dialogs when clicked. Click the second button to remove those listeners from the first button.</p>
            
            <button type="button" id="button-one">Click for alerts</button>
            <button type="button" id="button-two">Remove listeners</button>

            【讨论】:

              【解决方案12】:
               var events = [event_1, event_2,event_3]  // your events
              
              //make a for loop of your events and remove them all in a single instance
              
               for (let i in events){
                  canvas_1.removeEventListener("mousedown", events[i], false)
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-06-29
                • 1970-01-01
                • 2018-09-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-08-03
                • 1970-01-01
                相关资源
                最近更新 更多