By Paul McFedries

One of the brow-furrowing problems you run into when using jQuery is trying to get an event handler to work on an element that you create with code. To see what this means, take a look at an example:
<button id="add-div-button">
Click to add the div

// Build the div element as a string and then prepend it
$('#add-div-button').click(function() {
var strDiv = ';
strDiv += 'Double-click to switch the text and background colors.';
strDiv += ';
// Set up the div with a double-click event handler
$('#my-div').on('dblclick', function() {
if($('#my-div').css('color') === 'rgb(255, 250, 205)') {
$('#my-div').css('color', 'darkgreen');
$('#my-div').css('background-color', 'lemonchiffon');
} else {
$('#my-div').css('color', 'lemonchiffon');
$('#my-div').css('background-color', 'darkgreen');

When you click the button, the first jQuery event handler builds a div element as a string and then uses prepend to add it to the body element. That div element uses the id value my-div. However, the second jQuery event handler is for a dblclick event on that same my-div element. Theoretically, the dblclick handler switches the element’s text and background colors, but if you try this example, you can double-click the div until your finger falls off and nothing will happen.

Why doesn’t the event handler handle anything? Because when the browser was loading the page and came upon the code for the dblclick event handler, the target — that is, the div with the id value my-div — didn’t yet exist, so the browser ignored that event handler.

To fix this problem, you use a jQuery technique called event delegation, which means you do two things:

  • You bind the event handler not to the element itself, but to an ancestor element higher up in the web page hierarchy. This needs to be an element that exists when the web browser parses the event handler.
  • Add an extra parameter to the on() method that specifies which element your click handler actually applies to.

Here’s the new syntax for the on() method:
$(ancestor).on(event, descendant, function() {
This code runs when the event fires

  • ancestor: A selector that specifies the ancestor element that is delegated to be monitored for the event
  • event: A string specifying the name of event you want the browser to listen for
  • descendant: A selector that specifies the descendant element of ancestor that’s the actual target of the event
  • function(): The callback function that jQuery executes when the event occurs

This version of the on() method delegates the event handler to the ancestor element. When the event fires, the ancestor element looks through its descendants until it finds the element or set given by descendant, and it then runs the handler with that element or set as the event target.

To fix the previous example, you could use the document object as the ancestor argument, and add #my-div as the descendant argument:
$(document).on('dblclick', '#my-div', function() {

When choosing which ancestor to use as the delegate, the best practice is to use the closest ancestor that exists when the browser processes the event handler. For example, if in the example you were appending the div to, say, an existing article element, it would be better to use that article element as the delegate than the document object. Why is it better, you ask? Because the further away the ancestor, the more descendants the ancestor has to run through before it finds the event target, which can be a real drag on performance.