Search code examples
javascriptjqueryevent-handlingdom-eventsdisabled-input

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


I am implementing a JS Event-Disabler class, to disable all Native and Programmable eventlisteners of a certain dom element and all its children.

So far I've been able to disable all JQuery events and the default browser events, but not the eventlisteners set like

document.getElementById('cin').addEventListener("click", function(){
        alert('I should not alert when disabled');
});

So clicking on the element ('native element') shouldn't alert, but it does. How do I stop that from happening, within my nothing function. If there is away to not even need to call another function but just disable all events then that would also be fine, but need to be able to re-enable all again.

Also, I can assure you that the nothing() function executes first.

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>


Solution

  • For anyone looking for the full Working Code: Here it is. Make sure you add the 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;    
    }