Skip to content

is() counts positional pseudo-classes within the match set, where jQuery's .is() counts across the document #140

Open

Description

@gravitypdf-bot

Description

is() counts a positional pseudo-class (:first, :last, :eq(), :lt(), :gt(), :odd, :even) within the match set. jQuery's .is() counts it across the whole document. For any selector that includes a positional pseudo-class, the two can give opposite answers:

$xml = '<?xml version="1.0"?><r><i id="a"/><i id="b"/><i id="c"/><i id="d"/></r>';
Call jQuery 3.7.1 QueryPath main
#c .is(':first') false true
#c .is('i:first') false true
#a .is('i:last') false true
#c, #d .is('i:eq(1)') false true
#a .is(':eq(1)') true false
#c .is('i:eq(2)') true false
#c .is(':eq(2)') false false

(:eq(1) on #a is true in jQuery because the document order is r, a, b, c, d.)

Why jQuery differs from filter()

In jQuery 3.7.1, .is() checks rneedsContext (traversing.js). For a positional selector it evaluates jQuery(selector) against the document and tests membership, so :first means the document's first match. .filter() doesn't do this: it counts positions within the set, and QueryPath's filter() already agrees with it (qp($xml, '#c')->filter('i:first') is c, as in jQuery).

So since #51/#128, QueryPath's is() behaves like jQuery's .filter(), not like .is(). is() calls NodeMatcher::matchesAny() on the match set (src/Helpers/QueryChecks.php).

Why it matters now

#131 (issue #118) makes the stop selector of nextUntil(), prevUntil() and parentsUntil() count across the document, because jQuery tests it with .is(). Once #131 is merged, qp($xml, '#a')->nextUntil(':eq(3)') and qp($xml, '#c')->is(':eq(3)') count positions differently in the same library. #131 adds NodeMatcher::matchesInDocument(), which is() could use: is() would be true if any element of the match set is in the document's matches.

Expected

Either:

  1. is() counts positional selectors across the document, as jQuery's .is() does. This is a behaviour change. The CHANGELOG entry for html5qp('<p><span>foo</span></p>')->is('span') returns true #51 already describes is() as behaving "like jQuery's .is()", or
  2. Keep the current behaviour and document in is()'s DocBlock that positional pseudo-classes count within the match set, unlike jQuery.

is() with a DOMNode or SplObjectStorage argument, and with non-positional selectors, is not affected.

QueryPath version

main at ed98764. jQuery values come from jQuery 3.7.1 on jsdom.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions