As somebody who (unfortunately) learned more of jQuery
than raw javascript
I am just now taking the time to replace all of my code with raw javascript
. No, it's not needed, but it's an easier way for me to learn. A problem I am facing is converting all of my $(document).on
with raw javascript
. My website is a "single-page application" and most of my actual HTML
is in different files which are called via Ajax
requests. So, my question is, how would I look for an event
fired from dynamically loaded content? I am assuming I would have to add an onclick
event to them, but how is it that jQuery
does it without needing an onclick
event
?

- 1,005
- 2
- 10
- 27
-
Do you want to know how AJAX callbacks work? Or just any JavaScript event callback? – Azmisov Jun 17 '15 at 01:13
-
All I want to know is how I can load dynamic content e.g. `Loaded` and then listen for when the user clicks on that element without adding an `onclick` attribute. – Polarize Jun 17 '15 at 01:16
-
Knowing how to do things in raw JS is valuable, but the things you can do with `$.on` are truly fantastic, `jQuery`'s eventing system is architected very well. Don't try and replace it with raw JS, it's too much effort. Read through the `jQuery` source if you want to know how it works, but don't reinvent the wheel. – Adam Jenkins Jun 17 '15 at 01:20
-
I suggest using a framework, such as backbone.js, Backbone has built in event delegation. – demux Jun 17 '15 at 01:20
-
And what browsers should that work in? What type of selectors, jQuery has a complete selector engine built in? What kind of events, not all events bubbles, jQuery fixes that. Event delegation in itself is rather simple, you attach the event to an element that exists and checks the events target, but there's a lot of things that jQuery has that makes event delegation easier. – adeneo Jun 17 '15 at 01:21
-
event delegation to the root element – Leo Jun 17 '15 at 01:21
-
7@Josh: Don't let people tell you it's too much work or not worth it. I started off with jQuery just like you but then broke away and learned the native DOM. Haven't looked back. Totally worth it. I build a micro library that I use. My code is as succinct as jQuery (or more at times), is much faster, and the library is only a few KB gzipped. People told me *"don't reinvent the wheel"*. So glad I ignored them. – Jun 17 '15 at 01:38
-
1I recommend to read http://www.quirksmode.org/js/introevents.html to learn more about event handling. – Felix Kling Jun 17 '15 at 02:24
6 Answers
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;
};
}

- 7,608
- 4
- 33
- 61
-
If only `matches()` was widely supported, that would be great. Today it hardly works in any browser without a prefix, I think it's only supported in Chrome 34 and the latest Firefox. – adeneo Jun 17 '15 at 01:23
-
1@adeneo: True, though you get pretty wide support, IE9+, with a simple shim. I'll add it. Thanks for the reminder. – Jun 17 '15 at 01:24
-
1
-
-
3
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

- 30,079
- 5
- 45
- 53
-
3`DOM0` syntax prevents you from adding multiple click handlers in multiple places. – Adam Jenkins Jun 17 '15 at 01:21
-
@Adam, are you talking about the difference between `onclick` and `addEventListener`? – AmmarCSE Jun 17 '15 at 01:22
-
2Yeah you shouldn't be using old onclick events you should be using event listeners wow -_- – EasyBB Jun 17 '15 at 01:30
-
How do we 'listen' to multiple events ? $('#id1,#id2,#id3').on("click mousedown mouseup", function(e) { }); – anjanesh Jul 27 '18 at 14:28
-
document.addEventListener('click mousedown mouseup', function(event) { alert("Event : " + event); alert("ID : " + event.target.id); }); doesn't work – anjanesh Jul 28 '18 at 17:11
-
@anjanesh check out https://stackoverflow.com/questions/8796988/binding-multiple-events-to-a-listener-without-jquery and http://jsfiddle.net/y3n1hge6/24/ – AmmarCSE Jul 29 '18 at 20:09
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>

- 14,448
- 4
- 33
- 54
Another approach for modern browsers would be something like this:
const on = (selector, event, handler, element=document) => {
element.addEventListener(event, (e) => { if(e.target.matches(selector)) handler(e); });
};
// click will work for each selector
on('[type="button"], .test, #test','click', e => {
alert(e.target.innerHTML);
});
// click event will work for nested .test3 element only
on('.test3','click', e => {
alert(e.target.innerHTML);
},document.querySelector('.test2'));
<div id="test">
test
</div>
<div class="test">
test 1
</div>
<div class="test">
test 2
</div>
<button type="button">
go
</button>
<div class="test3">
test 3 outer
</div>
<div class="test2">
<div class="test3">
test 3 inner
</div>
test 2
</div>
I would offer a very small improvement over the fantastic accepted answer:
function add_event(el, name, callback, selector) {
if (selector === undefined) {
el.addEventListener(name, callback);
}
else {
el.addEventListener(name, function(event) {
var t = event.target;
while (t && t !== this) {
if (t.matches(selector)) {
callback.call(t, event);
}
t = t.parentNode;
}
});
}
}
By switching the last 2 parameters around, you can recover the default addEventListener behavior when you leave out the selector.

- 837
- 9
- 19
I want to add and suggest a simple jQuery like on method:
It's similar to @benvc answer with an improvement which is binding the handler to this
pointing to the attached element.
The solution uses Element.closest
which is widely supported. If you want to support older browsers you can add a polyfill
//The on() function:
const on = (ele, type, selector, handler) => {
ele.addEventListener(type, (event) => {
let el = event.target.closest(selector);
if (el) handler.call(el, event); //The element is bind to this
});
};
//Example:
on(document, 'click', '.any-selector', function(event) {
console.log(this, event.target);
// this -> The .any-selector element.
// event.target -> The firing element a descendent of any-selector
});

- 6,500
- 3
- 27
- 48