Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Rolando Lopez and Ryan Jeffords
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Rolando Lopez Ryan Jeffords

Sharing Event Handlers Across jQuery Event Bindings To A Single Element

By
Published in Comments (10)

This really just isn't my week when it comes to jQuery event management. Yesterday, I found out that I had a complete misassumption as to how jQuery passed around Event objects; now, this morning, I discovered that I have another misassumption at to how jQuery manages event bindings to a single element. In particular, I don't have a clear picture as to how the additional event data that one might provide during an event binding gets associated with the given event handler. This became obvious when I tried to share a pre-defined event handler between two event bindings on the same element for the same event type.

To demonstrate my misunderstanding in jQuery event binding, I have created a very simple demo. In the following code, I have defined a stand-alone Javascript function to act as an event handler. Then, I grab a paragraph element and bind a "click" event to it twice, each binding using the same pre-defined function as its event handler. The only difference between the separate event bindings is that they each provide unique additional event information to be available in the subsequent "event.data" collection.

<!DOCTYPE HTML>
<html>
<head>
	<title>Sharing Event Handlers In jQuery Event Binding</title>
	<script type="text/javascript" src="jquery-1.4a1.js"></script>
	<script type="text/javascript">

		// When the DOM is ready, initialize the document.
		jQuery(function( $ ){

			// Gather all paragraphs.
			var paragraphs = $( "p" );


			// Define the event handler method that will be used
			// in both of the bindings below.
			var clickHandler = function( event ){
				console.log( "Which:", event.data.whichHandler );
			};


			// Bind first click handler. NOTICE that we are using
			// additional event data.
			paragraphs.bind(
				"click",
				{
					whichHandler: "first"
				},
				clickHandler
			);


			// Bind second click handler. NOTICE that we are
			// using different, additional event data from above.
			paragraphs.bind(
				"click",
				{
					whichHandler: "second"
				},
				clickHandler
			);

		});

	</script>
</head>
<body>

	<p>
		Katie, your wet t-shirt is incredibly provocative!
	</p>

</body>
</html>

As you can see, each "click" event binding uses the same event handler, clickHandler; but, each event binding provides a different "whichHandler" data point. When I click the paragraph within the above page, I get the following console output:

Which: second

As you can see, only one of the two "click" event handlers was triggered and it used the last-bound event data collection. It turns out that event binding on a given element has to be unique; and, that only the event type and event handler play a part in that uniqueness - any additional event data provided to the event binding is coincidental and not part of the actual binding definition.

As with my previous jQuery event binding misconception, this makes perfect sense in hindsight. If you look at the way jQuery allows you to unbind a specific event handler, you'll see that it allows you to use an event type and event handler:

unbind( type, fn )

If this form of the unbind() invocation targets a specific event handler (which it does), then event-binding-uniqueness can only be defined as a combination of event type and event handler. Taking this into account, in order to fix my above demo, I would have to wrap my shared event handler in unique, anonymous function decorators:

<!DOCTYPE HTML>
<html>
<head>
	<title>Sharing Event Handlers In jQuery Event Binding</title>
	<script type="text/javascript" src="jquery-1.4a1.js"></script>
	<script type="text/javascript">

		// When the DOM is ready, initialize the document.
		jQuery(function( $ ){

			// Gather all paragraphs.
			var paragraphs = $( "p" );


			// Define the event handler method that will be used
			// in both of the bindings below.
			var clickHandler = function( event ){
				console.log( "Which:", event.data.whichHandler );
			};


			// Bind first click handler. NOTICE that we are using
			// additional event data. To ensure that our event
			// binding is unique, I am wrapping the handler in its
			// own unique, anonymous function.
			paragraphs.bind(
				"click",
				{
					whichHandler: "first"
				},
				function( event ){
					return( clickHandler( event ) );
				}
			);


			// Bind second click handler. NOTICE that we are
			// using different, additional event data from above.
			// To ensure that our event binding is unique, I am
			// wrapping the handler in its own unique, anonymous
			// function.
			paragraphs.bind(
				"click",
				{
					whichHandler: "second"
				},
				function( event ){
					return( clickHandler( event ) );
				}
			);

		});

	</script>
</head>
<body>

	<p>
		Katie, your wet t-shirt is incredibly provocative!
	</p>

</body>
</html>

As you can see in the above code, I am taking the pre-defined event handler, clickHandler(), and wrapping it in an anonymous function. When doing this, I just have to be sure to pass along the event object and return the resultant value. Wrapping the event handler in this way creates the desired uniqueness per-event-binding, and when we click on the paragraph this time, we get the following console output:

Which: first
Which: second

As you can see this time, each "click" event handler was triggered and referenced its own unique event data.

This might seem like an "out there" problem, and is indeed one that I have only just run into after a long period of jQuery usage; but, I came across it when researching custom jQuery event types that needed to be customized per-binding on a given element. Now that I have a better understanding of how jQuery event binding works, I think I might have to rethink the custom event binding stuff I've been looking into... but more on that later.

Want to use code from this post? Check out the license.

Reader Comments

29 Comments

Interesting observation Ben. I didn't know you could provide additional information with an event-binding and it would have made sense if it has created a new event handler when this information changes. Looking at unbind explains it all. Although it is a bit of a dodgy design choice.

15,848 Comments

@Martin,

I suppose it's really an outlier of an issue. I mean who would have thought that the same exact handler would be bound twice to the same element for the same event :) I only ran into it because I am experimenting with custom jQuery events that can be customized per-binding.

We'll see what I can do with that.

3 Comments

@Ben... :) I've run into this before...
As far as being able to unbind the events after the fact, check this out...
http://docs.jquery.com/Namespaced_Events

so your script block from above would look like the following...
<!--script-->
jQuery(function( $ ){

// Gather all paragraphs.
var paragraphs = $( "p" );


// Define the event handler method that will be used
// in both of the bindings below.
var clickHandler = function( event ){
console.log( "Which:", event.data.whichHandler );
};


// Bind first click handler. NOTICE that we are using
// additional event data. To ensure that our event
// binding is unique, I am wrapping the handler in its
// own unique, anonymous function.
paragraphs.bind(
"click.namespace1",
{
whichHandler: "first"
},
function( event ){
return( clickHandler( event ) );
}
);


// Bind second click handler. NOTICE that we are
// using different, additional event data from above.
// To ensure that our event binding is unique, I am
// wrapping the handler in its own unique, anonymous
// function.
paragraphs.bind(
"click.namespace2",
{
whichHandler: "second"
},
function( event ){
return( clickHandler( event ) );
}
);

});
<!--/script-->

Then if you wanted to unbind say the second one...
you would use the following syntax...

paragraphs.unbind('click.namespace2');

15,848 Comments

@Timothy,

Ah, very interesting. I have not used name spaces before, but it seems cool, especially when you can single out event handlers that are created by a given plugin.

I think I might be able to leverage this namespace idea to work with my custom event experiment. Thanks!

1 Comments

Another interesting problem is: what happens there is a html binded event handler, and using jQuery I assign another one?

<input id="x" onclick="something();" ...>

$("#x").click(another);

or - how can I call the original handler?

15,848 Comments

@Hernyák,

That's an interesting question. I don't know off the top of my head. Typically, I stick to one style (jQuery or non-jQuery). I am pretty sure you can't double-up with things like body/onload. But, inline clicks might be able to exist in parallel with jQuery events. Not really sure.

1 Comments

I am using Namespaced events but seem to be having the same problem. The second binding in the example below does not get binded -

jQuery('foo.bar', foo);
jQuery('foo.bar2', foo);

When 'foo.bar2' is triggered, the callback function foo does not receive the event.

Using jQuery 1.3.2. I have to wrap in an anonymous function as well.

15,848 Comments

@Dan,

This seems to work for me. This is what I am doing - to make sure we are on the same page as to what is being discussed:

var f = function( event ){ alert( event.type ); }

$( "body" )
.bind( "bar.foo", f )
.bind( "bar.foo2", f );

$( "body" ).trigger( "bar.foo2" );
$( "body" ).trigger( "bar" );

The first only triggers the second binding. The latter triggers both. Is that what you are talking about?

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel