Bug Tracker

Modify

Ticket #9747 (closed bug: invalid)

Opened 3 years ago

Last modified 3 years ago

:first selector behaves inconsistently in chained find() invocations

Reported by: hallettj Owned by:
Priority: low Milestone: 1.next
Component: selector Version: 1.6.1
Keywords: Cc:
Blocking: Blocked by:

Description (last modified by rwaldron) (diff)

Affects every version of jQuery that I tested: 1.2.6, 1.3.2, 1.4.4, 1.5.2, 1.6.1.

Reproduced in Chrome beta under Ubuntu 11.04.

Steps to reproduce:

  1. Create HTML structure with multiple elements, such as a list.
  2. Construct a jQuery selector that selects some or all of those elements, such as $('li').
  3. Use a find() invocation on the previously selected elements with a selector that matches some nested element and include the expression ":first" in that selector. For example, $('li').find('a:first').

Expected results:

The first matched nested element is returned from the find() invocation. In the case of a list where nested elements are single anchor tags, the anchor tag in the first list element is returned.

Actual results:

Every nested element matching the given selectors is returned. It is as though ":first" has no effect on result of the find() invocation.

jsfiddle test case:

If I combine both selectors into one jQuery() call instead of using a chained find() invocation then the result is as I expect. For example, $('li a:first') returns a single anchor tag: the one in the first list item. But $('li').find('a:first') returns every anchor tag in every list item - assuming that each list item has at most one anchor tag.

Maybe this is intended behavior. But it is really not what I expected. If this is intended behavior it would be helpful to have a note in the documentation for :first, :eq, or find() that explains the discrepancy.

Change History

comment:1 follow-up: ↓ 2 Changed 3 years ago by rwaldron

  • Priority changed from undecided to low
  • Resolution set to invalid
  • Status changed from new to closed
  • Component changed from unfiled to selector

I reduced your test even further and there is nothing inconsistent about it:  http://jsfiddle.net/rwaldron/XBM2w/

comment:2 in reply to: ↑ 1 Changed 3 years ago by hallettj

Replying to rwaldron:

I reduced your test even further and there is nothing inconsistent about it:  http://jsfiddle.net/rwaldron/XBM2w/

The inconsistency is that I expected $(selectorA + " " + selectorB) to be equivalent to $(selectorA).find(selectorB) in every case. Finding out that that is not true is very surprising. If this is really not the way that find() is supposed to work there should be an explanation in the documentation as the current behavior is not obvious.

comment:3 Changed 3 years ago by rwaldron

The description in the api is quite clear:

Description: Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.

comment:4 Changed 3 years ago by hallettj

What is not clear is the scope of the selectors in the find() call. Consider that, at least in a case where every 'li' has an 'a' descendant, $('li a:first') and $('li:first a') produce the same results. This implies that ':first' scopes over the entire result set, not just to the results of traversing an individual 'li' tag. So when I run $('li').find('a:first') I expect ':first' to limit the entire result set to one element. But instead I get three elements. This time ':first' *is* scoped to the results of traversing an individual 'li' tag. To my view the scope of ':first' is inexplicably inverted when used in find().

I understand now that find() probably makes separate selections against the descendants of each 'li' tag and that ':first' is probably scoped to the results each individual selection. But I was only able to come to that conclusion because of my investigation of find() behavior for this issue report.

The documentation for ':eq()' says,

Select the element at index n within the matched set.

It does not give any indication that in the case of find() there will be a separate "matched set" corresponding to each element in the original selection, and that ':eq()' will apply to each such set individually before those sets are unioned and returned. But the actual behavior of ':first' and ':eq()' in find() seems to indicate that this is a more accurate description.

I do not believe that the docs provide any information to clarify this point. Furthermore, in my opinion the behavior as it is implemented is counter-intuitive and is a bug.

Last edited 3 years ago by hallettj (previous) (diff)

comment:5 Changed 3 years ago by rwaldron

  • Keywords needsdocs added
  • Description modified (diff)

Just because you says it's a bug, does not make it a bug.

comment:6 Changed 3 years ago by dmethvin

  • Keywords needsdocs removed

Consider that, at least in a case where every 'li' has an 'a' descendant, $('li a:first') and $('li:first a') produce the same results. This implies that ':first' scopes over the entire result set, not just to the results of traversing an individual 'li' tag.

No it doesn't. Look at the case where the first 'li' does not have an 'a' descendant.  http://jsfiddle.net/dmethvin/PSnPQ/

To my view the scope of ':first' is inexplicably inverted when used in find().

The find() method finds one "first" in each element of the matched set that it works on, assuming there is one at all.

In any case, it's worked this way since jQuery 1.0 and we've never had any other confusion about it, so I'm going to remove the needsdocs. Any attempt to explain would need to be duplicated across all the positional selectors (eq, lt, gt, first, last) and IMO only make the docs more difficult to understand.

Last edited 3 years ago by dmethvin (previous) (diff)

comment:7 Changed 3 years ago by rwaldron

Dave, while I'm 100% in agreement with you, what do you think about a single page that offers insight as you have above re: positional selectors. That way we could link all of those docs to a single reference?

Please follow the  bug reporting guidlines and use  jsFiddle when providing test cases and demonstrations instead of pasting the code in the ticket.

View

Add a comment

Modify Ticket

Action
as closed
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.