Event Binding in jQuery

I’ll preface this with the fact that I am still learning through Launch School, and more than happy to be corrected. If you see anything unclear or incorrect, please let me know.

Overview

Our goal with this blog post is to break down what happens when you bind an event to a function using jQuery. While the process itself seems simple, it is helpful to understand more about what is involved in the code and what occurs every step of the way.

First, we’ll go through event binding in a simple case, and then we’ll add a few notes about how event binding is often done in an object, and why event binding works the way it does in an object.

Simple Event Binding in jQuery

In jQuery we bind events in objects in the following manner:

var $p = $('p');
$p.on('click', function() {
console.log(this);
});

The sequence below occurs when the user interacts with any p element on the page, due to event binding:

  1. User actions cause the browser to generate events.
  2. An event is generated on the DOM element where the event occurred.
  3. The handler function is called whenever an event is generated, and if there is an event listener, then that event listener’s callback function is executed in the context of the DOM element on which the event was generated.

Using our example with the click event bound to the p element above, the sequence would look something like:

  1. User hits the mouse button while the mouse is placed over a paragraph element.
  2. A click event is generated on that paragraph element.
  3. The anonymous function in our code above logs out the p element on which the click event occurred, and we get something like the below logged into our console:
<p>Lorum ipsum</p>

This sequence is executed because of a few key elements of jQuery event binding; the selector, the on method, the event handler, and the context of execution. Let’s break these down and go through the meat of each, starting with the jQuery selector.

jQuery Selector

The jQuery selector gives us the foundation on which to bind our event. Without selecting a DOM element and making it into a jQuery object, we wouldn’t have anything to which to bind events.

We won’t go into too much detail to describe different selectors and how they are put together, since more information can be found in the documentation. Suffice to say that depending upon whether you are using a selector to find a class, element, id, etc., jQuery might use a different JavaScript method under the hood to select matches from the DOM. For selecting an element, it uses JavaScript’s getElementsByTagName.

In many cases when using jQuery, you’ll see a variable defined in the fashion below. For now, let’s skip the left side and our variable name. We’ll come back to it shortly.

var $p = $('p');

On the right side of our =, the $ is an alias for the jQuery function, which “searches through the DOM for any elements that match the provided selector and creates a new jQuery object that references these elements”, according to our trusty jQuery documentation. As long as you have loaded jQuery you have access to this function — though the DOM must be loaded also, for you to be able to interact with any elements.

Back to the left side and our variable name. Using the $ to begin our variable name is convention with jQuery, as it quickly tells other developers that the variable is referencing a jQuery object. You do not have to name your variable this way, but I echo the recommendation because of the clarity it offers.

Our jQuery selector collects all p’s into a jQuery object and thereby allows us to use the on method to perform our event binding. Without further ado, let’s discuss the on method.

jQuery $.on

Referring to jQuery documentation for the on method:

The .on() method attaches event handlers to the currently selected set of elements in the jQuery object. As of jQuery 1.7, the .on()method provides all functionality required for attaching event handlers.

From jQuery version 1.7 forward, the on method has been the recommended method for both direct event binding and creating delegated events. For this reason, we are going to focus on the mechanism of the on method alone and none of the deprecated methods for binding. The template for our on method is illustrated in our documentation as:

on(events [, selector][, data], handler)

From this template we can see that we need the event (or space-separated events) and the event handler arguments to pass to our on method, and that we have the ability to pass in much more. The range of possibilities is beyond the scope of this post, but for the simple demonstration we will focus on the arguments we must pass to the on method, the event (or space-separated events) and the event handler.

In our example, we so far know that we have all of our p elements collected into a jQuery object, and that we have attached a singular type of event, a click event, to our jQuery object, $p.

The Event Handler Function and Context

The event handler function is clearly articulated in the jQuery documentation for on as, “A function to execute when the event is triggered.” While our function is an anonymous function, we could just as easily have passed a named function as our callback function:

var $p = $('p');
$p.on('click', function() {
console.log(this);
});
// could be written as:
$p.on('click', logIt);
function logIt() {
console.log(this);
}

The reason that we need to make this point explicit is that the context in which we define our logIt function is different than that in which our anonymous function is defined. Context is an essential consideration for the event handler, as it is with any function. However, we must remember that it is only the context of execution that matters, not the context in which a function is defined. So while we have defined the logIt method outside of the context of the selected$p object on which on is called, that does not matter.

To be clear, when we invoke our function as $p.on('click', logIt);, we have executed the function within the context of our selected jQuery object, regardless of where logIt was defined. Our anonymous function, likewise, is bound to the context of the selected jQuery object when it is called, and so we also log the this of our anonymous function as the specific jQuery object in $p that is clicked, since jQuery binds the context to the element for us.

This behavior regarding the context of execution and this is why, despite the discrepancy in where we define our functions, we don’t have to alter the code up to the event handler ($p.on('click', ...) at all to achieve the same end with either. Since we are binding our event handler function execution to the jQuery object in either case, it does not matter that the logIt function is defined in another context.

Similarly, instead of using this, we could have used e.target provided we passed an argument e to our function:

var $p = $('p');
$p.on('click', function(e) {
console.log(e.target);
});

You might be wondering where this e argument came from, and why we can simply log the target property on it in order to get the same affect as if we had logged this. The key is in, again, the documentation. This time we turn to documentation for the jQuery event object, which is “…guaranteed to be passed to the event handler”. The target property is normalized across browsers and is simply the DOM element that initiated the event.

Because the event object is guaranteed to be passed to the event handler, we could even write our code:

var $p = $('p');
$p.on('click', function() {
console.log(arguments[0].target);
});

For clarity we probably would not want to write our code using arguments[0], however, passing the arguments array-like object to our log method demonstrates that the event object is getting passed into our function whether we explicitly pass an argument or not.

Now that we have an understanding of the basics of jQuery event binding, we can delve a little further into context with a few other examples.

Context and ‘This’ in Event Binding

Let’s put together a little situation so that we can examine how event binding works when using jQuery in the wild. As you will see, all of that action is contained by a little bit of code such as that below.

Click on the button on the boat to move the boat to the right, or the squid to move the squid to the right.

What we notice is that there is only one event listener, and yet we can click on multiple items and the code will still run. That is because we do not permanently bind the execution context with the on method, rather we bind the event handler function to the context of the jQuery object that registered the event.

In our example, regardless of which anchor tag you click, an animate function is executed. The context of that animation is based solely on the event target (i.e. this).

<style>
...omitted for brevity, used to style the squid and boat...
</style>
<div id="ocean">
<div id="boat">
<a href="#">S.S. Boat</a>
</div>
<div id="squid"><a href="#">00<br><span>||||||||</span></a></div>
</div>
<script>
// whether you click the squid link or the boat button, each will move right
$('a').on('click', function(e) {
e.preventDefault();

$(this).closest('div').animate({
left: "+=40"
}, 1000 )
});
</script>

Binding Events in Objects

If we have an object such as our ItemList object written below and in our CodePen further down, then our binding takes on another dimension:

var ItemList = {
count: 0,
add: function(e) {
e.preventDefault();
    this.count++;
this.showCount();
},
showCount: function() {
$('#count span').text(this.count);
},
bindEvents: function() {
$('#add').on('click', this.add.bind(this));
},
init: function() {
this.bindEvents();
}
};
ItemList.init();

The mechanism is essentially the same, but we need to ensure we provide an explicit context for executing our function. Depending upon how you set your code up, the global object might not have access to your method, or just as bad, the object you think you’re referencing with this might be an entirely different object than what is actually referred to with this.

The code can be more easily viewed below:

In our OLOO event binding, we have to permanently bind the context in order for our object’s method to be executable in that object’s context.

We have to permanently bind the add method to the ItemList object when binding an event to a method that is on that object. In our case, we bind our events when the object is initialized with ItemList.init(), because our event binding is carried out through the bindEvents method on the object, which is executed within the ItemList.init method:

var ItemList = {
// omitted for brevity
bindEvents: function() {
$('#add').on('click', this.add.bind(this));
},
init: function() {
this.bindEvents();
}
};
ItemList.init();

As a result, we use the context of this, which is the ItemList object itself, both to get access to the add method and to pass as the context to which we want to bind the execution: this.add.bind(this). That means that we are able to execute our add function and reference the count property of the ItemList object, as well as execute the showCount method on the ItemList object:

var ItemList = {
count: 0,
add: function(e) {
e.preventDefault();

this.count++;
this.showCount();
},
showCount: function() {
$('#count span').text(this.count);
},
// omitted for brevity
};

Now, if we were to log the e.target and compare it with this, we would find that the two are distinct. While the this is the ItemList object to which we’ve bound it, e.target is the DOM element that registered the event. By binding our event with bind, we’ve forced the context of execution to remain the ItemList object, and therefore overridden the equality between this and e.target.

Summary

Binding events in jQuery is a relatively straightforward process in simple cases, but there are many moving parts. We did not touch on event delegation, more complex use cases for the on method, or the variety of ways you might use event binding to accomplish your ends.

If you find you are interested in diving into event binding in jQuery, I recommend you read through the documentation. Some of the reference pages I found most helpful are:

I also recommend looking through some of the JavaScript resources on MDN that cover the meat of event binding, and also what goes on under the hood of jQuery:

I hope you enjoyed this post. Please feel free to reach out if I explained anything poorly or with some good examples or insights I might want to add!