Skip to main content

Bug Tracker

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 dmethvin comment:1

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-code matchesSelector or our own quick optimization for tag#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.

Changed February 09, 2012 10:45PM UTC by dmethvin comment:2

status: newopen

Changed February 09, 2012 10:48PM UTC by dos-one 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 dmethvin 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 dos-one 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 dmethvin comment:6

Yes, you would want to stop propagation, right?

Changed February 09, 2012 11:22PM UTC by dos-one 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 dmethvin 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 dos-one 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 dmethvin comment:10

component: unfiledevent
milestone: None1.8
priority: undecidedlow

We should either fix or wontfix for 1.8.

Changed June 18, 2012 03:31PM UTC by dmethvin comment:11

owner: → dmethvin
status: openassigned

Changed June 27, 2012 04:12PM UTC by Dave Methvin comment:12

resolution: → fixed
status: assignedclosed

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 dmethvin comment:13

#11993 is a duplicate of this ticket.

Changed July 24, 2012 08:37PM UTC by dmethvin comment:14

#12114 is a duplicate of this ticket.

Changed October 15, 2012 01:50PM UTC by anonymous comment:15

What version is this fixed in?