How to use $ .on in pure JavaScript: “$ (…) .on (event, selector, function)”?

Posted on

Question :

This is in jQuery we have on , any element <a> with class test e without the foo class will fire the function when clicked, even though you create the element after the event is already added:

$("#new").click(function () {
    $('<p><a class="test" href="#">Novo: (' + (new Date) + ')</a></p>').appendTo("#container");

$("#container").on("click", "a.test:not(.foo)", function () {
<script src=""></script><buttonid="new">Adicionar novo</button><br>

<div id="container">
   <p><a href="#">Oi (não funciona)</a></p>

Note that in testing only the elements added later work, ie things like document.querySelector().forEach will not work, unless you use MutationObserver , but then this would still be a third behavior .

At first I thought jQuery used MutationObserver , but after a few tests I realized that actually the event is actually in document and #foobar , so how I have to use Vanilla.js I started trying recreate this, I used Element.addEventListener + , it looked something like:

var container = document.getElementById("container");
var newBtn = document.getElementById("new");

on(container, "click", "a.test:not(.foo)", function () {
    console.log("achou:", this);

newBtn.onclick = function () {
    var n = document.createElement("p");

    n.innerHTML = '<a class="test" href="#">Novo: (' + (new Date) + ')</a>';


function on(target, type, selector, callback)
    target.addEventListener(type, function (e)
         var el =,
             els = document.querySelectorAll(selector);

         for (var i = 0, j = els.length; i < els.length; i++) {
             if (els[i] === el) {
       , e); //Passa o elemento como this e o event como primeiro argumento
<button id="new">Adicionar novo</button><br>

<div id="container">
   <p><a href="#">Oi (não funciona)</a></p>

However this does not seem very performative, the doubt is as follows:

  • Is there any way to test a specific element with a queryselector ?

Answer :

This is delegation of events with delegate element associated with a more complex CSS selector.

$(document).on("click" % means that the event handset is tied to document . So we already have document.addEventListener .

Then we need to treat to know what the event was, was in that element, or a descendant. For this we need to check the selectors. We can use .matches() that checks if a given element hits a given CSS selector, and associate it with the :not() that is already supported by modern browsers to ensure that the wrong class is not accepted.

We could do this like this:

document.addEventListener('click', function(e) {
  var correto ='a.test:not(.foo)');
  // ...

Example with tests:

document.addEventListener('click', function(e) {
  var correto ='a.test:not(.foo)');
  // o resto é só para o exemplo
  var seletor = [,' ')].filter(Boolean).join('.');
  console.log(seletor, '|', correto ? 'encontrado!' : 'falhou...',;

a {
  display: inline-block;
  margin: 5px;
  padding: 10px;
  border: 1px solid #005;
  width: 10px;

a:hover {
  color: #aaf;
  cursor: pointer;
<a class="qq-coisa">A</a>
<a class="test foo">B</a>
<a class="foo">C</a>
<a class="test">D</a>


An addition to Sérgio’s answer is the prefixes question, Element.matches is not supported by some older browsers, or had a different name (such as matchesSelector ).

Browsers and Element.matches

Until Chrome 33, Opera 15 and Safari 7.0 used the prefix:


Firefox up to version 33 used:


Internet Explorer uses since version 9 (no version uses matches ):


Opera Presto from 11.5 used:


Alternative to the Element.matches and prefixed

Older browsers may support querySelectorAll , but may not support matches and prefixed as webkitMatchesSelector , for this the

Leave a Reply

Your email address will not be published. Required fields are marked *