Side navigation
#11269 closed bug (invalid)
Opened February 01, 2012 06:49PM UTC
Closed February 01, 2012 06:51PM UTC
Last modified February 01, 2012 07:43PM UTC
Event delegation with .on() will not work when a jQuery object is supplied for the Selector parameter
Reported by: | warrenparsons | Owned by: | |
---|---|---|---|
Priority: | undecided | Milestone: | None |
Component: | unfiled | Version: | 1.7.1 |
Keywords: | Cc: | ||
Blocked by: | Blocking: |
Description
Event delegation with .on() will not work when a jQuery object is supplied for the Selector parameter.
It seems to ignore the parameter in this case, and skips delegation altogether. The result is that 'this' refers to the object to which .on() is attached, rather than the element(s) specified in the Selector param.
This may be intended behavior, but it is somewhat unexpected.
Test cases: http://jsfiddle.net/tUkwZ/1/
Tested with: 1.7.1 (included with test case), 1.7.2b1 (tested locally)
Verified in: Chrome (current) and Firefox 9.0.1 - both Mac
Attachments (0)
Change History (7)
Changed February 01, 2012 06:51PM UTC by comment:1
resolution: | → invalid |
---|---|
status: | new → closed |
Changed February 01, 2012 07:09PM UTC by comment:2
_comment0: | Hi Dave, \ \ I think the test case didn't make the problem I'm seeing clear enough. \ \ In my real-world use case, I need to delegate because the elements in question are sometimes added to the document after page load, and it would be useful to pass in a jQuery object as my selector is rather complex. \ \ Here is the selector I'm using: \ \ \ {{{ \ var $links = $('a[href^="http"], a[rel="new-window"]').not('.colorbox, .addthis-button'); \ }}} \ \ This would be pretty ugly to write as a pure CSS selector. Also, the API docs for the :not() selector ([http://api.jquery.com/not-selector/]) include the following: \ \ ''The .not() method will end up providing you with more readable selections than pushing complex selectors or variables into a :not() selector filter. In most cases, it is a better choice.'' \ \ Being able to pass in a jQuery object rather than only a string can, in cases like this, make code more readable. It also has potential benefits when selectors are dynamically constructed, since they can be built with jQuery objects rather than put together as a string. \ \ Thanks for listening. → 1328123403308982 |
---|---|
_comment1: | Hi Dave, \ \ I think the test case didn't make the problem I'm seeing clear enough. \ \ In my real-world use case, I need to delegate because the elements in question are sometimes added to the document after page load, and it would be useful to pass in a jQuery object, as my selector is rather complex. \ \ Here is the selector I'm using: \ \ \ {{{ \ var $links = $('a[href^="http"], a[rel="new-window"]').not('.colorbox, .addthis-button'); \ }}} \ \ This would be pretty ugly to write as a pure CSS selector. Also, the API docs for the :not() selector ([http://api.jquery.com/not-selector/]) include the following: \ \ ''The .not() method will end up providing you with more readable selections than pushing complex selectors or variables into a :not() selector filter. In most cases, it is a better choice.'' \ \ Being able to pass in a jQuery object rather than only a string can, in cases like this, make code more readable. It also has potential benefits when selectors are dynamically constructed, since they can be built with jQuery objects rather than put together as a string. \ \ Thanks for listening. → 1328124135264803 |
Realized I probably need to reply, rather than just add a comment.
Changed February 01, 2012 07:22PM UTC by comment:3
Replying to [comment:1 dmethvin]:
Yep, as documented it must be a selector string. If you are going to select the elements in advance you might as well bind them.
Hi Dave,
I think the test case didn't make the problem I'm seeing clear enough.
In my real-world use case, I need to delegate because the elements in question are sometimes added to the document after page load, and it would be useful to pass in a jQuery object, as my selector is rather complex.
Here is the selector I'm using:
var $links = $('a[href^="http"], a[rel="new-window"]').not('.colorbox, .addthis-button');
This would be pretty ugly to write as a pure CSS selector. Also, the API docs for the :not() selector (http://api.jquery.com/not-selector/) include the following:
''The .not() method will end up providing you with more readable selections than pushing complex selectors or variables into a :not() selector filter. In most cases, it is a better choice.''
Being able to pass in a jQuery object rather than only a string can, in cases like this, make code more readable. It also has potential benefits when selectors are dynamically constructed, since they can be built with jQuery objects rather than put together as a string.
Thanks for listening.
Changed February 01, 2012 07:31PM UTC by comment:4
You're misunderstanding the point at which a selector selects. Once your $links variable is set, it contains specific DOM elements. If you add more to the document later, they will not be in $links. The selector string was only used to obtain the DOM elements that are now in $links. It does not represent the set of elements that currently exist in the document matching the selector string originally passed to $()
at some point in the past. The reason a selector string is required is so that we can determine if the element *currently* matches the selector when an event occurs. If you do not want to repeat the long selector, just assign the selector string to a variable.
Changed February 01, 2012 07:35PM UTC by comment:5
Replying to [comment:4 dmethvin]:
Crap. You're right of course.
Sorry for the false alarm.
Changed February 01, 2012 07:39PM UTC by comment:6
_comment0: | And actually, for a complex case like that I would just do the delegation manually. Directly bind an event to a parent and manually check whether the complex criteria are met, e.g., \ {{{ \ var $elem = $(event.target); \ if ( this.nodeName.toLowerCase() != "a" ) \ return; \ if ( $elem.hasClass("colorbox") || $elem.hasClass("addthis-button") ) \ return; \ ... etc ... \ }}} \ → 1328125213270135 |
---|
And actually, for a complex case like that I would just do the delegation manually. Directly bind an event to a parent and manually check whether the complex criteria are met, e.g.,
var $elem = $(event.target); if ( event.target.nodeName.toLowerCase() != "a" ) return; if ( $elem.hasClass("colorbox") || $elem.hasClass("addthis-button") ) return; ... etc ...
Changed February 01, 2012 07:43PM UTC by comment:7
Replying to [comment:6 dmethvin]:
And actually, for a complex case like that I would just do the delegation manually. Directly bind an event to a parent and manually check whether the complex criteria are met, e.g.,> var $elem = $(event.target); > if ( event.target.nodeName.toLowerCase() != "a" ) > return; > if ( $elem.hasClass("colorbox") || $elem.hasClass("addthis-button") ) > return; > ... etc ... >
That's a good call.
In my (somewhat sheepish) defense, I wrote the core of this function three years ago, without any attempt at delegation. I've let it evolve over time, allowing me to overlook its incompatibility with delegation as currently written, when I should instead have just rewritten it from scratch.
Thanks.
Yep, as documented it must be a selector string. If you are going to select the elements in advance you might as well bind them.