JS 事件禁用器:无法阻止本机事件侦听器执行

JS Event Disabler: Can't stop native eventlisteners from executing

我正在实现一个 JS 事件禁用器 class,以禁用某个 dom 元素及其所有子元素的所有本机和可编程事件侦听器。

到目前为止,我已经能够禁用所有 JQuery 事件和默认浏览器事件,但不能禁用像

这样设置的事件监听器
document.getElementById('cin').addEventListener("click", function(){
        alert('I should not alert when disabled');
});

所以点击元素 ('native element') 不应该提醒,但确实如此。 在我的 nothing 函数中,如何阻止这种情况发生。 如果甚至不需要调用另一个函数而只是禁用所有事件,那也可以,但需要能够再次重新启用所有事件。

此外,我可以向您保证 nothing() 函数首先执行。

var tellme = function(who) {
  //console.info('Event by: '+who+' @'+Date.now());   
  alert('Event by: ' + who + ' @' + Date.now());
}
$(window).load(function() {
  /* SOME FUNCTION TO ENSURE OUR FUNCTIONS ARE THE FIRST TO BE CALLED */
  $.fn.bindFirst = function(name, fn) {
    this.on(name, fn);
    this.each(function() {
      var handlers = $._data(this, 'events');
      for (var key in handlers) {
        if (handlers.hasOwnProperty(key)) {
          var listeners = handlers[key];
          if (listeners.length > 1) {
            var lastEvent = listeners.pop();
            listeners.splice(0, 0, lastEvent);
            if (listeners[1].handler.name === lastEvent.handler.name)
              listeners.splice(1, 1);
          }
        }
      }
    });
  };

  function shouldbenothing() {
      tellme('native catcher');
      nothing();
    }
    /* THE DO NOTHING FUNCTION, NEEDS SOMETHING MORE, DOESN'T CANCEL ALL*/

  function nothing() {
    event.cancel = true;
    event.preventDefault();
    event.stopPropagation();
    event.stopImmediatePropagation();
    //Needed for Jquery
    throw new Error("NOT AN ERROR: Just forcefully stopping further events @" /*+Date.now()*/ ); //Add the Date.now to see that this code does run before the native function.
    return false;
  }

  /* THIS WILL ONLY RETURN NON-NATIVE EVENTS, ONLY PROGRAMMED EVENTS*/
  function getAllActiveEvents(element) {
    var result = [];
    var handlers = $._data(element, 'events');
    for (var key in handlers) {
      if (handlers.hasOwnProperty(key)) {
        result.push(key);
      }
    }
    return result.join(' ');
  }

  function getAllEvents(element) {
    var result = [];
    for (var key in element) {
      if (key.indexOf('on') === 0) {
        result.push(key.slice(2));
      }
    }
    return result.join(' ');
  }

  /*SOME PROGRAMMED EVENTS, BESIDES THE NATIVE ONES*/

  $('input').on('keyup', function() {
    $('#text').html(this.value);
  });
  $('p').on('click', function() {
    $('#text').html(this.innerHTML);
    tellme('jquery');
  });
  document.getElementById('jsE').addEventListener("click", function() {
    tellme('p:js');
  });
  document.getElementById('cin').addEventListener("click", function() {
    tellme('input:js');
  });

  /* THE ACTUAL DISABLER CODE */
  /*TOGGLE TO ACTIVE OR DISABLE EVENTS FROM TAKING PLACE NATIVE AND EXTRA*/
  var isOn = false;
  $('button').on('click', function() {
    if (isOn)
      $("#obj *").each(function() {
        $(this).off(getAllEvents($(this)[0]), "", nothing);
        $("#obj").css('pointerEvents','');
      });
    else {
      $("#obj *").each(function() {
        var elem = $(this)[0];
        var events1 = getAllActiveEvents(elem); //Only programmed listeners
        var events2 = getAllEvents(elem); //Native + other listeners
        $(this).bindFirst(events2, nothing);
      });
      $("#obj").css('pointerEvents','none');
    }
    isOn = !isOn;
    this.innerHTML = isOn;
  });
});
p {
  cursor: pointer;  
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<style>p {pointer:hand;}</style>
<div id="obj">
  <p>jquery event</p>
  <p id="jsE">js event</p>
  <p onclick="tellme('native');">native event</p>
  <input id='cin' type="text" />
  <p id="text">3</p>
</div>
<p>not catched</p>
<input type="text">

<button>toggle</button>

可能有一个非常简单、非 js、纯 css 的解决方案……像这样:

.whatever {
    -webkit-user-select:none;
    -moz-user-select:none;
    -ms-user-select:none;
    user-select:none;
    pointer-events:none;
}

...只需将 whatever-class 添加到您想要完全禁用用户交互的任何元素。

所以我很快就找到了解决办法。 通过使用 css 代码,我可以禁用所有相关的鼠标事件。然而,这不会停止本机事件,比如如果您要通过 JS 触发事件,但至少它会从用户的角度停止它。

我实际上也更喜欢 css 方法,因为它确实允许我仍然交互和触发事件,例如,当我想向用户展示一些东西而不受用户干扰时。

css代码:

//To Disable    
$("#obj").css('pointerEvents','none');
//To Enable
$("#obj").css('pointerEvents','');

任何正在寻找完整工作代码的人:这里是。 确保添加 css.

/* Event Disabler, disables all events */
/* How to use:
*    Toggle Events: toggleEvents(selector);
*    Disable all Events: toggleEvents('body',true);
*    Enable all Events: toggleEvents('body',false);
*/
var toggleEvents = null;
$(window).load(function(){
    /* SOME FUNCTION TO ENSURE OUR FUNCTIONS ARE THE FIRST TO BE CALLED */
    $.fn.bindFirst = function(name, fn) {
        this.on(name, fn);
        this.each(function() {
        var handlers = $._data(this, 'events');
        for (var key in handlers) {
            if (handlers.hasOwnProperty(key)) {
                var listners = handlers[key];
                if (listners.length > 1) {    
                    var lastEvent = listners.pop();
                    listners.splice(0, 0, lastEvent);          
                    //Removes duplicate eventListners
                    if (listners[1].handler.name === lastEvent.handler.name)
                        listners.splice(1, 1);
                }
            }
        }
        });
    };
    /* THE DO NOTHING FUNTION CANCELS ALL EVENTS, EVEN BY TRIGGERED*/
    function nothing() {
        event.cancel = true;
        event.preventDefault();         
        event.stopPropagation();
        event.stopImmediatePropagation();
        event.bubbles = false;    
        if(window.event){
           window.event.cancelBubble=true;
        }
        //throw new Error("NOT AN ERROR: Forcefully stopping further events");
        return false;
    }
    function getAllActiveEvents(element) {
        var result = [];
        var handlers = $._data(element, 'events');
        for (var key in handlers) {
            if (handlers.hasOwnProperty(key)) {
                result.push(key);
            }
        }
        return result.join(' ');
    }
    function getAllEvents(element) {
        var result = [];
        for (var key in element) {
            if (key.indexOf('on') === 0) {
                result.push(key.slice(2));
            }
        }
        return result.join(' ');
    }
    
    var enabled = false;
    toggleEvents = function(selector,flag) {
        enabled = flag === undefined ? !enabled : flag;
        if (enabled) {
            $(selector+" *").each(function(){ 
                //Only programmed and attached listners
                var events1 = getAllActiveEvents($(this)[0]); 
                //All Native events attached or not
                var events2 = getAllEvents($(this)[0]); 
                $(this).bindFirst(events2, nothing );                
            });  
            //Disabled most user pointer events
            $(selector).addClass('eventsDisabled');    
        } else {
            $(selector+" *").each(function() {
                $(this).off(getAllEvents($(this)[0]), "", nothing );
            });
            $(selector).removeClass('eventsDisabled');
        }
    };
});
.eventsDisabled {
    -webkit-user-select:none !important;
    -moz-user-select:none !important;
    -ms-user-select:none !important;
    user-select:none !important;
    pointer-events:none !important;    
}