Re: Behavior of matches() and closest() with :scope()

On 4/09/14 7:54 PM, Anne van Kesteren wrote:
> On Tue, Sep 2, 2014 at 6:53 PM, Boris Zbarsky <bzbarsky@mit.edu> wrote:
>> ...
> So let's see. If we don't pass a :scope elements argument we get
>
>    E.matches(":scope") -> false
>    E.closest(":scope") -> null
>    E.closest(":has(> :scope)" -> null
>
> If we pass E as :scope elements we get
>
>    E.matches(":scope") -> true
>    E.closest(":scope") -> E
>    E.closest(":has(> :scope)" -> E's parent
>
> Now for closest() we could also pass the current ancestor A as :scope
> elements, then we get
>
>    E.closest(":scope") -> E
>    E.closest(":has(> :scope)" -> null
>
> It seems to me that passing the element on which the method is invoked
> as :scope elements (i.e. E) makes the most sense. That argues for 1).
>
>

It doesn't sound like you are considering the explicit passing of a 
scope element yet, but for when you do, here is a use-case for 
element.closest(scopedSelector, ancestor).

Say I want to use event delegation by registering an event listener on 
ancestor (A) to respond to events that would have bubbled through some 
descendant (D) which matches a selector scoped to A, say ":scope > 
li.enabled > a[href]".

Now say D (in this case <a href="...">) has a child <span> element (E) 
on which a click occurs.

When the click event propagates to the listener on A, it has a target of 
E and a currentTarget of A.
I want to find if it bubbled through some descendant D. Ideally I could 
call:
     D = E.closest(':scope > li.enabled > a[href]', A);
or even with a relative selector
     D = E.closest('> li.enabled > a[href]', A);

The explicit or implicit ':scope' would refer to A.

For this use-case, the upwards DOM traversal for matching would also 
terminate on reaching **the- scope-boundary**, A. This seems intuitive 
to me, and implies that
     E.closest(scopedSelector) // no explicit scope element
is the equivalent of
     E.closest(scopedSelector, document-or-fragment-or-root)

Or maybe it implies that the ':scope' selector is a nonsense for 
.matches() and .closest() when there is no explicit scope element.

I can't recall for sure, but I thought the original discussion of 
matches() did conclude that if there was an explicit scope element
     E.matches(selector, A)
then selector was a scoped-selector - loosely the inverse of 
A.query(selector) - and otherwise
     E.matches(selector)
the selector was unscoped - and loosely the inverse of 
A.querySelector(selector)

I would expect:
     E = A.query('> ul > li'); // assuming E exists ...
     E.matches('> ul > li', A) // true

And I would expect:
     E = A.querySelector('ul > li'); // assuming E (a descendant of A) 
exists ...
     E.matches('ul > li') // true

I might even expect:
     E = A.query(':scope ul > li'); // assuming E exists ...
     E.matches(':scope ul > li') // true - equivalent of 
E.matches(':scope ul > li', document)

But I wouldn't expect:
     E = A.query(':scope ul > li'); // assuming E exists ...
     E.matches(':scope ul > li') // false - equivalent of 
E.matches(':scope ul > li', E)

Sean

Received on Thursday, 18 September 2014 03:47:19 UTC