Event delegation is an easier and more performant way to add event listeners in JavaScript. You attach the listener to a common ancestor and allow the event to "bubble" up through the DOM tree.

Some folks like to add listeners near the top of the DOM tree: on the window, document, document.documentElement, or document.body.

I used to do this, but now I prefer to attach the listener to the closest common ancestor. I don't use jQuery, but their documentation for the on() method says that this is the best approach:

Attaching many delegated event handlers near the top of the document tree can degrade performance. Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path from the event target up to the top of the document. For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents.

Given the following HTML, imagine that when one of the three paragraphs is clicked, I want to log its textContent to the console:

<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
<p>This is yet another paragraph.</p>
<small>Copyright &copy; Example</small>

Instead of attaching my listener to the document or document.body, I'd attach it to the main element, since that's the closest common ancestor:

// Get the main element
var main = document.querySelector("main");

* Log a paragraph's textContent
* @param {Object} event The Event object

function logText (event) {

// If not a paragraph, do nothing
if (event.target.tagName !== "P") return;

// Log the paragraph's textContent


// Log paragraphs' textContent on click
main.addEventListener("click", logText);

I'm not saying you should never add event listeners close to the top of the DOM tree. If you have lots of event handlers in many different places, it's quicker, easier, and more performant to do it that way.

But if you only need to listen for an event at a specific part of the DOM, it makes sense to move your listener further down the tree. 👍

UPDATE: I've written a helper function to make this easier!