API DOM Events: делегирование событий и stopPropagation

Это код с JQuery 1.7:

<div class="test">
  <div class="bu">
    <a>
      bu here
    </a>
  </div>
</div>
<script src="http://code.jquery.com/jquery-1.7.js"></script>
<script>
$(document).on('click', '.test', function () { alert(0); return false; });
$(document).on('click', '.bu', function () { alert(1); return false; });
$(document).on('click', '.bu', function () { alert(2); return false; });
</script>

Нажатие на .test>.bu предупредит "1" и предупредит "2", но не предупредит "0"

Мой вопрос: как сделать то же самое БЕЗ jQuery (на нативном DOM API)? Кажется, я не могу сделать это с Native DOM API, не реализовав свою собственную библиотеку...

Ответ 1

<div class="a">
    <div class="b">
      <div class="c" style="border: 1px solid silver; width: 80px; text-align: center;line-height: 80px;">
          click me!
      </div>
    </div>
</div>

<script>

// Element.prototype.matchesSelector
(function (x) {
  var i;
  if (!x.matchesSelector) {
    for (i in x) {
      if (/^\S+MatchesSelector$/.test(i)) {
        x.matchesSelector = x[i];
        break;
      }
    }
  }
}(Element.prototype));

Document.prototype.on =
Element.prototype.on = function (eventType, selector, handler) {
  this.addEventListener(eventType, function listener(event) {
    var t = event.target,
      type = event.type,
      x = [];
    if (event.detail && event.detail.selector === selector && event.detail.handler === handler) {
      return this.removeEventListener(type, listener, true);
    }
    while (t) {
      if (t.matchesSelector && t.matchesSelector(selector)) {
        t.addEventListener(type, handler, false);
        x.push(t);
      }
      t = t.parentNode;
    }
    setTimeout(function () {
      var i = x.length - 1;
      while (i >= 0) {
        x[i].removeEventListener(type, handler, false);
        i -= 1;
      }
    }, 0);
  }, true);
};

Document.prototype.off =
Element.prototype.off = function (eventType, selector, handler) {
  var event = document.createEvent('CustomEvent');
  event.initCustomEvent(eventType, false, false, {selector: selector, handler: handler});
  this.dispatchEvent(event);
};

document.on('click', '.b', function () {
  alert(2);
});
document.on('click', '.a', function () {
  alert(1);
});
document.on('click', '.b', function (event) {
  alert(3);
  event.stopPropagation();
});

</script>

Ответ 2

Здесь вы идете:

document.addEventListener( 'click', function ( e ) {
    if ( hasClass( e.target, 'bu' ) ) {            
        // .bu clicked
        // do your thing
    } else if ( hasClass( e.target, 'test' ) ) {
        // .test clicked
        // do your other thing
    }   
}, false );

где hasClass -

function hasClass( elem, className ) {
    return elem.className.split( ' ' ).indexOf( className ) > -1;
}

Live demo: http://jsfiddle.net/Nrxp5/30/

Ответ 3

Вот версия на основе прототипа

Document.prototype.on = function(event, target = null, callBack){
    this.addEventListener(event, function(event){
        let len = target.length, i = 0;
        while(i < len){
            if(event.target === target[i]){
                callBack.call(target[i], event);
            }
            i ++;
        }
    }, false);
};

Использование такое же, как у jQuery:

let btns = document.getElementsByTagName('button');
document.on('click', btns, function(event){
    console.log(this.innerText)
});

И живой пример ниже:

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body bgcolor="#000">
<button>hello1</button>
<button>hello2</button>
<script type="text/javascript">
	Document.prototype.on = function(event, target = null, callBack = function(){}){
		this.addEventListener(event, function(event){
			let len = target.length, i = 0;
			while(i < len){
				if(event.target === target[i]){
					callBack.call(target[i], event);
				}
				i ++;
			}
		}, false);
	};


//example of usage
	let btns = document.getElementsByTagName('button');
	document.on('click', btns, function(t){
		alert(this.innerText);
	})

//add elements after delegating an event
	let newBtn = document.createElement('button');
		newBtn.innerText = 'btnNew';
		document.body.appendChild(newBtn);
    
//delay to create
setTimeout(function(){
let newBtn = document.createElement('button');
		newBtn.innerText = 'another btnNew';
		document.body.appendChild(newBtn);
},2000);
</script>
</body>
</html>