Bug Tracker

Ticket #11315 (closed bug: fixed)

Opened 3 years ago

Last modified 2 years ago

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:
Blocking: Blocked by:

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.

Change History

comment:1 Changed 3 years ago by dmethvin

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.

comment:2 Changed 3 years ago by dmethvin

  • Status changed from new to open

comment:3 Changed 3 years ago by dos-one

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)

comment:4 Changed 3 years ago by dmethvin

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.

comment:5 Changed 3 years ago by dos-one

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!

comment:6 Changed 3 years ago by dmethvin

Yes, you would want to stop propagation, right?

comment:7 Changed 3 years ago by dos-one

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!

comment:8 Changed 3 years ago by dmethvin

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.

comment:9 Changed 3 years ago by dos-one

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.

comment:10 Changed 3 years ago by dmethvin

  • Priority changed from undecided to low
  • Component changed from unfiled to event
  • Milestone changed from None to 1.8

We should either fix or wontfix for 1.8.

comment:11 Changed 2 years ago by dmethvin

  • Owner set to dmethvin
  • Status changed from open to assigned

comment:12 Changed 2 years ago by Dave Methvin

  • Status changed from assigned to closed
  • Resolution set to fixed

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

comment:13 Changed 2 years ago by dmethvin

#11993 is a duplicate of this ticket.

comment:14 Changed 2 years ago by dmethvin

#12114 is a duplicate of this ticket.

comment:15 Changed 2 years ago by anonymous

What version is this fixed in?

Note: See TracTickets for help on using tickets.