Javascript equivalent to $.on

Here is a javascript equivalent to on()

jQuery

$(document).on('click', '#my-id', callback);

function callback(){
   ...handler code here
}

Javascript

document.addEventListener('click', function(event) {
    if (event.target.id == 'my-id') {
      callback();
    }
});
function callback(){
   ...handler code here
}

With this approach, the idea is to make use of event.target. Of course, as the selector changes, your code will have to get more involved


Binding handlers in native API is done using addEventListener().

To emulate jQuery's event delegation, you could fairly easily create a system that uses the .matches() method to test the selector you give.

function delegate(el, evt, sel, handler) {
    el.addEventListener(evt, function(event) {
        var t = event.target;
        while (t && t !== this) {
            if (t.matches(sel)) {
                handler.call(t, event);
            }
            t = t.parentNode;
        }
    });
}

There are probably some tweaks to be made, but basically it's a function that takes the element to bind to, like document, the event type, a selector and the handler.

It starts on the e.target and traverses up the parents until it gets to the bound element. Each time, it checks to see if the current element matches the selector, and if so, it invokes the handler.

So you'd call it like this:

delegate(document, "click", ".some_elem", function(event) {
    this.style.border = "2px dashed orange";
});

Here's a live demo that also adds dynamic elements to show that new elements are picked up as well.

function delegate(el, evt, sel, handler) {
    el.addEventListener(evt, function(event) {
        var t = event.target;
        while (t && t !== this) {
            if (t.matches(sel)) {
                handler.call(t, event);
            }
            t = t.parentNode;
        }
    });
}

delegate(document, "click", ".some_elem", function(event) {
    this.parentNode.appendChild(this.cloneNode(true));
    this.style.border = "2px dashed orange";
});
<div>
  <p class="some_elem">
    <span>
      CLICK ME
    </span>
  </p>
</div>

Here's a shim to add a bit more support for .matches().

if (!Element.prototype.matches) {
  Element.prototype.matches = 
    Element.prototype.matchesSelector || 
    Element.prototype.webkitMatchesSelector ||
    Element.prototype.mozMatchesSelector ||
    Element.prototype.msMatchesSelector || 
    Element.prototype.oMatchesSelector || 
    function(s) {
        var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i = matches.length;
        while (--i >= 0 && matches.item(i) !== this) {}
        return i > -1;            
    };
}

In modern browsers, you can use Element.closest() to simplify replication of jQuery's .on() method as well as ensure that you capture event bubbling from children of the targeted element (a nuance that some other implementations overlook). Older browsers, including IE, would require a polyfill for this to work.

const on = (element, type, selector, handler) => {
  element.addEventListener(type, (event) => {
    if (event.target.closest(selector)) {
      handler(event);
    }
  });
};

on(document, 'click', '#test', (event) => console.log('click'));
<button id="test">
  Clickable
  <i>Also Clickable</i>
</button>