Side navigation
#11315 closed bug (fixed)
Opened February 09, 2012 09:45PM UTC
Closed June 27, 2012 04:12PM UTC
Last modified October 15, 2012 01:50PM UTC
Problems with delegate() and :first in nested elements with equivalent classes
Reported by: | dos-one | Owned by: | dmethvin |
---|---|---|---|
Priority: | low | Milestone: | 1.8 |
Component: | event | Version: | 1.7.1 |
Keywords: | Cc: | ||
Blocked by: | Blocking: |
Description
If you have a nested hierarchy of DOM elements, all having the same class. .delegate() using the :first pseudo selector has problems. Please see the JSBin below.
http://jsbin.com/uxunim/9/edit
It appears that only the topmost element is found. This presents major problems when dealing with tree like dom structures. Further, if you switch between jQuery 1.6.4 and 1.7.1 in the JS Bin, you'll notice that everything works fine with 1.6.4 so this appears to be a clear regression.
Attachments (0)
Change History (15)
Changed February 09, 2012 10:40PM UTC by comment:1
Changed February 09, 2012 10:45PM UTC by comment:2
status: | new → open |
---|
Changed February 09, 2012 10:48PM UTC by comment:3
I understand your concerns regarding performance, although in some cases this is a very useful selector and the performance is not relevant. What would be an equivalent workaround? Further, doesn't it seem odd to say that you can only use the :first selector in some places that take a selector but not others?
We noticed this bug because we have a nested hierarchy (similar to a folder tree in Windows Explorer) so we have the same dom elements nested within each other. We can't just delegate to .class because there are multiple nested elements that all contain that class and we want the first one in relation to a given scope. (Further, we're using Backbone View's events pattern so we're somewhat limited in what we can do)
Changed February 09, 2012 10:51PM UTC by comment:4
If they are all first children of their parent you could use :first-child
, that is a standard selector and definitely a better semantic match as well. It may be possible to make :first
work and I'm looking at it, but I cringe to think of what the browser does for that case.
Changed February 09, 2012 11:00PM UTC by comment:5
So I've updated the JSBin (jsFiddle was doing their cloud move) to show our scenario with first-child, and we get some unfortunate event bubbling. So if you click "b" you also see that "a" got an event. Perhaps I'm doing something wrong though!
http://jsbin.com/uxunim/10/edit
In any case, if you guys do decide to go forward with a fix please definitely leave a comment.
Thanks for your quick replies!
Changed February 09, 2012 11:02PM UTC by comment:6
Yes, you would want to stop propagation, right?
Changed February 09, 2012 11:22PM UTC by comment:7
Well, we could, but in our situation that's really no different than just delegating to .class and stopping propagation. The nice thing about :first was that it didn't bubble the event when in a nested hierarchy like that.
(If you switch the JS bin to use :first and then use 1.6.4 that's the lack of bubbling we were relying on)
In any case, if this becomes an issue we cannot work around we'll likely just stick to the 1.6.x series. Thanks very much for your time!
Changed February 10, 2012 12:16AM UTC by comment:8
The nice thing about :first was that it didn't bubble the event when in a nested hierarchy like that.
Not true. When you click on the innermost link, the event continues to bubble from #c
to #b
to #a
and requires jQuery to do all the selector-matching at each element; it just didn't match the selector in 1.6.4. Nobody is ever calling .stopPropagation()
on your behalf.
Changed February 10, 2012 12:48AM UTC by comment:9
You're quite right, I could have said that in a more technically correct way. But I hope you can see that the point I was trying to make was that the
callback functions for the outer elements never got called when you click the inner element because, as you say, the selector didn't match. This is the behavior we were relying on, and this is the behavior that changed between versions.
Changed March 10, 2012 02:01PM UTC by comment:10
component: | unfiled → event |
---|---|
milestone: | None → 1.8 |
priority: | undecided → low |
We should either fix or wontfix for 1.8.
Changed June 18, 2012 03:31PM UTC by comment:11
owner: | → dmethvin |
---|---|
status: | open → assigned |
Changed June 27, 2012 04:12PM UTC by comment:12
resolution: | → fixed |
---|---|
status: | assigned → closed |
Fix #11315. Selector for .on() is relative to delegateTarget.
This fixes a regresssion from 1.6.4. Be aware that nearly every place that this bug comes into play, the selector in use is incredibly inefficient.
Changeset: 94e744aec9d25bb64a3cb72c3b81dd95e94d3955
Changed June 29, 2012 02:05PM UTC by comment:13
#11993 is a duplicate of this ticket.
Changed July 24, 2012 08:37PM UTC by comment:14
#12114 is a duplicate of this ticket.
Changed October 15, 2012 01:50PM UTC by comment:15
What version is this fixed in?
Yes, this did change. The 1.7 event delegation code now uses
.is()
internally rather than having its own implementation. The.is()
code is always rooted at the document so that selector always returns the first element in the document.Regardless, positional selectors like
:first
are non-standard and so they have to go through the JavaScript Sizzle engine rather than either the browser's native-codematchesSelector
or our own quick optimization fortag#id.class
. As a result any selector like.class:first
is horrendously inefficient; it has to get the complete list of elements with that class and see if there are any. And it does that for every element from the event.target to the delegation point.So, my preference would be to just say that positional selectors aren't supported in delegated event handlers. We can add more code to fix the regression but it will still be slow and unadvisable.