Bug 18912 - [Shadow]: ShadowRoot needs to support elementFromPoint
Summary: [Shadow]: ShadowRoot needs to support elementFromPoint
Status: RESOLVED FIXED
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: HISTORICAL - Component Model (show other bugs)
Version: unspecified
Hardware: PC All
: P2 normal
Target Milestone: ---
Assignee: Dimitri Glazkov
QA Contact: public-webapps-bugzilla
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 18428
  Show dependency treegraph
 
Reported: 2012-09-18 21:49 UTC by Daniel Freedman
Modified: 2012-11-29 21:26 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Freedman 2012-09-18 21:49:51 UTC
document.elementFromPoint(x,y) returns a reference to the element at (x,y). However, the children of a ShadowRoot, and the ShadowRoot itself, will return the host of the ShadowRoot. This leaves no way to find an shadow'd element at (x,y) without manual hitbox testing in javascript.

Note: "light dom" elements from insertion points work correctly with document.elementFromPoint
Comment 1 Daniel Freedman 2012-09-18 21:59:40 UTC
Sorry, forgot to add this part:

I propose that an 'elementFromPoint(x,y)' be added to ShadowRoot that returns the element at (x,y) inside the shadow.
Comment 2 Dominic Cooney 2012-09-19 14:14:23 UTC
What should it do given shadow.elementFromPoint(x, y) when there is a projected element at that point?

In cases where elementFromPoint would return the root element, does it make sense to return the ShadowRoot?
Comment 3 Daniel Freedman 2012-09-19 18:00:46 UTC
I think that returning the shadow root in that instance makes sense. The projected elements are not children of the ShadowRoot, and I think the developer should know that. Also, the projected elements will be correctly found with document.elementFromPoint, so there is still a way to find the element.
Comment 4 Dominic Cooney 2012-09-20 04:14:09 UTC
FWIW I think there are four plausible candidates here:

1. the shadow root
2. null (the projection is a "hole")
3. the content or shadow element that projected the element
4. the parent of #3 (which may be the shadow root in some cases)

(In reply to comment #3)
> I think that returning the shadow root in that instance makes sense.

Makes sense.

One thing that is tricky is that you won’t be able to distinguish between the host element being at that point (I assume that returns the shadow root?) and a projected element being at that point.

> The
> projected elements are not children of the ShadowRoot, and I think the
> developer should know that.

+1 to this.

> Also, the projected elements will be correctly
> found with document.elementFromPoint, so there is still a way to find the
> element.

That is true when the light child is in the document. However if you have an element in shadow DOM that itself has a shadow, eg this case:

<u>

{u’s shadow root}
  <v>
    <w>

{v’s shadow root}
  <content>

then elementFromPoint(x, y) pointing to <w> will require some scrabbling around to work out that you need to ask {u’s shadow root}.elementFromPoint to get it, and not document nor {v’s shadow root}.
Comment 5 Daniel Freedman 2012-09-24 19:09:47 UTC
The primary use case I see for elementFromPoint is inside of an event handler.
Since one cannot trust the projected children inside of ShadowRoot, it makes sense that one should not try to add an event listener to a ShadowRoot's projected children.
In that case, the most logical spot to add an event listener would be in the ShadowRoot that contains the element, which would then have the correct scope for an elementFromPoint call.

Now, there is a larger problem here involving touch events. In the implementation of touch events, touchmove does not change target.
This means that while the finger is moving on the screen, the same element that received the touchstart event will receive all the touchmoves (and touchend).
In this environment, if a finger is placed outside of a ShadowRoot and moved into it, the ShadowRoot will never receive the touchmove event, and cannot call its version of elementFromPoint.

This is a major problem for any polyfilled event systems (like PointerEvents: https://github.com/toolkitchen/PointerEvents).
Comment 6 Dominic Cooney 2012-10-01 03:43:42 UTC
(In reply to comment #5)
> In that case, the most logical spot to add an event listener would be in the
> ShadowRoot that contains the element, which would then have the correct scope
> for an elementFromPoint call.

Events get routed through <content> and <shadow> elements, so returning the <content> or <shadow> element is more precise than the shadow root.

However since <content> and <shadow> are not themselves rendered, it gives you the weird situation where you ask shadow.elementAtPoint(10, 50) and the offset/client properties of the result say the element is at (0,0)-(0,0).

Would that be a problem?

It seems like since you are already writing code that is dealing with ShadowRoot to call shadow.elementFromPoint instead of document.elementFromPoint, you could handle <content> and <shadow> specifically if you needed to.

> Now, there is a larger problem here involving touch events. In the
> implementation of touch events, touchmove does not change target.
> This means that while the finger is moving on the screen, the same element that
> received the touchstart event will receive all the touchmoves (and touchend).
> In this environment, if a finger is placed outside of a ShadowRoot and moved
> into it, the ShadowRoot will never receive the touchmove event, and cannot call
> its version of elementFromPoint.
> 
> This is a major problem for any polyfilled event systems (like PointerEvents:
> https://github.com/toolkitchen/PointerEvents).

The problem with touch is too mind boggling for me. It feels like a separate issue. If I understood what you wrote, it seems this problem would happen if the touch is moved over an ordinary div, irregardless of whether it has Shadow DOM.
Comment 7 Daniel Freedman 2012-10-01 20:00:11 UTC
(In reply to comment #6)
> (In reply to comment #5)
> > In that case, the most logical spot to add an event listener would be in the
> > ShadowRoot that contains the element, which would then have the correct scope
> > for an elementFromPoint call.
> 
> Events get routed through <content> and <shadow> elements, so returning the
> <content> or <shadow> element is more precise than the shadow root.
> 
> However since <content> and <shadow> are not themselves rendered, it gives you
> the weird situation where you ask shadow.elementAtPoint(10, 50) and the
> offset/client properties of the result say the element is at (0,0)-(0,0).
> 
> Would that be a problem?

No, that would be fine for my case. A developer would have the same sort of differences in the offset/client properties if the element in question was transformed.

> 
> It seems like since you are already writing code that is dealing with
> ShadowRoot to call shadow.elementFromPoint instead of
> document.elementFromPoint, you could handle <content> and <shadow> specifically
> if you needed to.

Agreed.

> 
> > Now, there is a larger problem here involving touch events. In the
> > implementation of touch events, touchmove does not change target.
> > This means that while the finger is moving on the screen, the same element that
> > received the touchstart event will receive all the touchmoves (and touchend).
> > In this environment, if a finger is placed outside of a ShadowRoot and moved
> > into it, the ShadowRoot will never receive the touchmove event, and cannot call
> > its version of elementFromPoint.
> > 
> > This is a major problem for any polyfilled event systems (like PointerEvents:
> > https://github.com/toolkitchen/PointerEvents).
> 
> The problem with touch is too mind boggling for me. It feels like a separate
> issue. If I understood what you wrote, it seems this problem would happen if
> the touch is moved over an ordinary div, irregardless of whether it has Shadow
> DOM.

The same scenario does occur when using only regular divs, but that problem is mitigated by dispatching a new event to the element found by EFP. In ShadowRoots, no shadowed children will be accessible by document EFP, so these elements cannot even have a new event sent to them in this manual fashion.
Comment 8 Hayato Ito 2012-10-02 07:25:32 UTC
(In reply to comment #4)
> FWIW I think there are four plausible candidates here:
> 
> 1. the shadow root
> 2. null (the projection is a "hole")
> 3. the content or shadow element that projected the element
> 4. the parent of #3 (which may be the shadow root in some cases)

I am wondering why we cannot return the projected children?
Does it relate to the lower-bound Encapsulation rule?
Comment 9 Dimitri Glazkov 2012-11-08 21:52:11 UTC
(In reply to comment #8)
> (In reply to comment #4)
> > FWIW I think there are four plausible candidates here:
> > 
> > 1. the shadow root
> > 2. null (the projection is a "hole")
> > 3. the content or shadow element that projected the element
> > 4. the parent of #3 (which may be the shadow root in some cases)
> 
> I am wondering why we cannot return the projected children?
> Does it relate to the lower-bound Encapsulation rule?

Since our event dispatch does not retarget at the lower boundary.
For consistency, given an element at x, y (let's call it HIT), I think we should:

1) return HIT if it's a descendant of an element, distributed in an insertion point
2) return <shadow> if HIT is in a nested tree.

WDYT?
Comment 10 Daniel Freedman 2012-11-09 18:49:43 UTC
Makes sense to me.

If a <shadow> is returned, and assuming the older shadow subtree is "public", one can recurse into it with <shadow>.olderSubtree.elementFromPoint(x, y).

This seems pretty usable.
Comment 11 Dimitri Glazkov 2012-11-28 23:58:57 UTC
Glen, I am going to plop this into the Shadow DOM spec for now, but we will need to align CSSOM a bit better.
Comment 12 Dimitri Glazkov 2012-11-29 21:26:03 UTC
http://dvcs.w3.org/hg/webcomponents/rev/fc9bfac4448a