Sharing Event Handlers Across jQuery Event Bindings To A Single Element

Posted December 18, 2009 at 9:24 AM by Ben Nadel

Tags: Javascript / DHTML

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.




Reader Comments

Dec 18, 2009 at 10:29 AM // reply »
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.


Dec 18, 2009 at 10:31 AM // reply »
11,238 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.


Dec 18, 2009 at 12:51 PM // reply »
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');


Dec 18, 2009 at 12:54 PM // reply »
11,238 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!


Mar 30, 2010 at 4:06 PM // reply »
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?


Apr 19, 2010 at 10:47 PM // reply »
11,238 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.


Jun 14, 2010 at 12:42 PM // reply »
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.


Jun 14, 2010 at 9:50 PM // reply »
11,238 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?


Jun 15, 2010 at 11:52 AM // reply »
2 Comments

This issue seems to have been sorted in jQuery 1.4x


Jun 16, 2010 at 9:02 AM // reply »
11,238 Comments

@Nick,

Ahh, ok, so it was just a bug for a bit. Thanks for the insight.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools