Events

Initiating and handling events in jQuery.

Scroll down...

Content

Resources

Comments

You just learned the guts of how JavaScript's event loop works and how it registers listener callbacks with your browser to catch them. In this lesson, you'll see the kinds of events that jQuery gives you access to and how you can attach your listeners effectively.

Our goal is for you to understand how to use jQuery events effectively and to get an idea of all the useful events that are out there waiting for you.

Attaching Listeners

As with most things, jQuery takes the bare-bones JavaScript listening functionality and makes it easier to use. In the last lesson, you saw how JavaScript uses the addEventListener function to register a new listener callback with your browser.

In contrast, the root function for all jQuery listeners is on. This allows you to register a listener for the current element "on" a particular event's firing. As with the plain JavaScript way, this just tells your browser (via the browser's API) that you want that particular callback to run every time the event is detected on that particular element.

It looks like this:

$( "p" ).on("click", function(){ console.log( "clicked!" ) });

jQuery provides you with convenience methods for each of the most common listeners, which can be called using the style above as well:

$( "p" ).click(function(){ console.log("clicked!") } );
$( "p" ).focus(function(){ console.log("focused!") } );
$( "p" ).blur(function(){ console.log("blurred!") } );
$( "p" ).change(function(){ console.log("changed!") } );

...and that's it. You can now register callback functions for any of your browser's events. Easy.

Listeners are Passive so Delegate

Just like a jQuery collection won't be able to detect new elements on the page after it is initially run, listeners that you set up will not be attached to new page elements which are added after you initially set up the listener. For instance, if you build a click listener for all <p> tags upon $(document).ready(), it will NOT be attached to any <p> tags you subsequently add to the DOM by using jQuery.

This will trip you up at some point. Just remember that listeners are dumb and passive -- they sit waiting for events and don't bother to go out and see if they should cover any new territory.

There is a way around this, though, which we'll talk about shortly. For a sneak peak, you can actually Delegate the listener to a parent element. Then, when your child element gets clicked, the event will also register with the parent. The (irresponsible but well-meaning) parent doesn't know how many children it has so it will gladly handle any events from its children, whether old or new. Again, we'll cover this in a minute so don't worry if it seems odd.

Event Bubbling aka Propagation

An essential factor to understand when working with events is how they actually propagate through the DOM. The last lesson covered the actual handling of method calls in JavaScript and how the JavaScript runtime environment chugs through the call stack and event queue. Here, we're more interested in how your browser actually handles events when they fire.

When you click on the web page in your browser window, your click registers with the top-most element (if it overlaps another one, for instance via absolute positioning) and/or the innermost (most specific) nested element. For example, the click event generated by clicking on a random paragraph on your page will be spawned by the appropriate <p> element. That event retains information that ties it to the specific <p> element that spawned it. We'll talk about this Event object down below.

It doesn't stop there, though. If it did, you might click on a paragraph and the events you registered for its enclosing <div> would never get fired. Instead, events propagate up through the DOM (like bubbling upwards). So the initial <p> gets the first crack at the event, then its enclosing <div>, then any more <div> elements, then the <body> tag, then the <html> tag and finally the Window.

Any event listeners you've set on those elements which are eligible for that event will be notified along the way. So, for instance, if you've set a click listener on all <div> elements and you click one that is nested inside another, it will bubble the event and run the callback function twice.

// Set up a simple DOM
<div>
  <div>
    click me!
  </div>
</div>

// Add a handler
$("div").click(function(){ console.log("Clicked!"); } );

// Click the inner div and see the console...
Clicked!   // the inner div catches the event
Clicked!   // the parent div catches the event

This is important behavior because you'll frequently add listeners to enclosing elements and you wouldn't want them to get blocked or shielded by the child elements contained inside of them.

On the flip side, it can also be annoying when you do want to block the enclosing element. Luckily that's easy to fix, as you'll see in a minute.

The Event Object

The callback function passed into an event listener is commonly called the "Event Handler" because that's exactly what it does. This function will often perform an action that is related to the event itself or its target element (e.g. our <p>). To help with this, all event listeners pass an argument to your callback function when the callback is invoked.

That object, called the Event Object, gives you lots of useful functionality. It has both properties and methods you can run on it to get more information about the event and its target.

Useful Event object properties include:

  • target -- the DOM element that first initiated the event (super useful!)
  • pageX,pageY -- the coordinates on the page where the event occurred
  • which -- the button or key that was pressed to generate the event
  • data -- any data that you associated with the event when setting up your listener
  • namespace and timeStamp

The available methods are:

  • stopPropagation() which cancels the propagation of the event through the DOM tree.
  • preventDefault() which prevents whatever default action the element would have done. This is particularly useful when working with links, where you might want a link to pop up a modal dialog box instead of redirecting to a new page.

For instance:

// Build a simple DOM
<ul id="the-only-ul">
  <li>
    Chocolate
  </li>
  <li>
    Potatoes
  </li>
</ul>


// Register a click event on a `<ul>`
// and log the event object
$( "#the-only-ul" ).click( function( eventObj ){ console.log(eventObj); } );


// When clicked, we get that full object with
// all its properties and methods
//=> jQuery.Event {originalEvent: MouseEvent, type: "click", isDefaultPrevented: function, timeStamp: 1415318229259, jQuery1110037576282396912575: true…}


// inspect just the useful properties
$( "#the-only-ul" ).click( function( eventObj ){
  console.log(eventObj.target);
  console.log(eventObj.pageX);
  console.log(eventObj.pageY);
  console.log(eventObj.which);
  console.log(eventObj.namespace);
  } );


// Now clicking reveals a bit more data
<li>​
  Chocolate       // `target`
</li>​
79                // `pageX` (pixels)
51                // `pageY` (pixels)
1                 // `which`
undefined         // `namespace`

A common pattern is to do something to the clicked element, for instance hiding it. To do so, wrap it in a jQuery object. Common practice is to refer to this object as $target now:

// Shifting up the syntax for variety
// `e` is still the event object
$("#some-div").on("click", function(e){

  $target = $(e.target);
  $target.hide();

});

See this learn.jquery.com article for a bit more detail on the handler and the event object that gets passed in.

Event Delegation

As we mentioned above, adding a listener to all the <p> tags on a page will not cover any new elements you add to the DOM. If you know that your child elements are likely to change but you still want to listen for events on them, you can Delegate the event to a parent element. This is also useful if you have many child elements and don't want the performance hit of giving them all event handlers.

"Delegate" is just a fancy way of saying "set up a listener on the parent but only for events on this particular type of child element". This is possible by passing on a second argument (between the name of the event and the callback) which filters down the child elements:

// Delegate our paragraph clicks to the
// parent div (which we assume is more stable)
$( "div.parent-of-p" ).on( "click", "p", function( event ) {
    alert( "Clicked the p!!!" );
});


// Setting up that delegated handler is *almost*
// the same as setting up the following event
// handler... but this version will not work for
// new DOM elements (since it only gets set up once)
// and it is much more performance-intensive if
// you have many `<p>` tags
$( "div.parent-of-p p" ).on( "click", function( event ) {
    alert( "Clicked the p!!!" );
});

The difference is subtle but important. Instead of binding clicks to a specific element which can be removed or modified, we bind to a parent instead. We still only allow clicks from <p> elements which are children of the parent because of the filter we specified.

It doesn't even need to be the parent -- any ancestor will do. If you want, you can attach all your event listeners directly to the <body> tag and filter them accordingly to detect the correct children. In practice, though, this can become brittle if you've got a lot of highly specific nesting in your DOM structure.

If delegation seems strange now, whenever you start working with elements that are being added to or removed from pages, a little voice in the back of your head should say "delegate your event handlers..."

To understand more, see the learn.jquery.com article on delegation.

Triggering an Event

To manually trigger an event from within the code, use trigger:

// fake a click on the first `<p>` element
$("p").first().trigger("click");

See the docs or the learn.jquery.com article for more detail.

Removing an Event Handler

You may not encounter it frequently, but to remove an event from an object, simply use the off function (instead of on...):

// remove a specific event
$( "div.parent-of-p p" ).off( "click")


// remove all attached events
$( "div.parent-of-p p" ).off()

off gets more specific than that, but you can always consult the docs if you encounter the need to do so.

Index of Useful Events

We mentioned several useful events already and you should consult the docs for a glance at the rest. For your convenience, we've compiled this list as a quick reference.

Here are a few of the most useful events from the docs, and you can probably guess their triggers from the names:

  • click
  • mouseenter (half of hover)
  • mouseleave (the other half of hover)
  • scroll
  • submit
  • select
  • blur
  • focus
  • keypress
  • keyup

Some of these events seem quite similar. Often the major difference is whether they apply to the boundaries of the element or also interactions within its limits (e.g. if you move the mouse from a child element back into the parent). You'll just have to get practice to tease apart the particular cases.

You'll get ample opportunity to use each of them shortly.

Additional Reading

You should read the introduction to events on learn.jquery.com for another angle on jQuery events and a bit more specificity in the gaps.

Browse through the on documentation here from the API.

Code Review

The important bits of code from this lesson

// The BAD way to attach an event handler
<button onclick="alert('Hello')">Say hello</button>

// Attach a jQuery event
$( "p" ).on("click", function(){ console.log( "clicked!" ) });

// Use the convenience shortcut method
$( "p" ).click(function(){ console.log( "clicked!" ) });

// Hide the targeted element
$("#some-div").on("click", function(e){
  $target = $(e.target);
  $target.hide();
});

// Delegate to a parent for performance and reliability
$( "div.parent-of-p" ).on( "click", "p", function( event ) {
    alert( "Clicked the p!!!" );
});

// Trigger an event
$("p").first().trigger("click");

// Remove a specific event
$( "div.parent-of-p p" ).off( "click")

// Remove all attached events
$( "div.parent-of-p p" ).off()

Wrapping Up

Events are incredibly useful when working with jQuery. Once you develop a mental model for hanging event handlers on elements and how events bubble through the DOM, you're pretty much all set. The rest is just getting practice with all the useful functions that are available to you.

For the most part, if you think there should be a particular kind of event (just like with the DOM manipulations we covered previously), Google it or consult the docs and you'll probably find it.



Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

Sorry, comments aren't active just yet!

Next Lesson: Callback Functions