Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM

On Mon, Mar 25, 2013 at 2:48 AM, Dominic Cooney <dominicc@chromium.org>wrote:

> On Sun, Mar 24, 2013 at 3:50 PM, Elliott Sprehn <esprehn@gmail.com> wrote:
>
>>
>> On Mon, Mar 18, 2013 at 4:48 AM, Dominic Cooney <dominicc@chromium.org>wrote:
>>
>>> ...
>>>
>>> I think the offset{Parent, Top, Left} properties should be adjusted.
>>> This means that in the above example, b.offsetParent would be <body> and
>>> b.offsetLeft would be silently adjusted to accumulate an offset of 10px
>>> from c. I think this makes sense because typical uses of offsetParent and
>>> offsetLeft, etc. are used to calculate the position of one element in the
>>> coordinate space of another element, and adjusting these properties to work
>>> this way will mean code that naively implements this use case will continue
>>> to work.
>>>
>>> This behavior is unfortunately slightly lossy: If the author had #c and
>>> wanted to calculate the position of #b in the coordinate space of #c, they
>>> will need to do some calculation to work it out "via" body. But presumably
>>> a script of this nature is aware of the existence of Shadow DOM.
>>>
>>> The question of what to do for offset* properties across a shadow
>>> boundary when the shadow *is* traversable is a vexing one. In this case
>>> there is no node disclosed that you could not find anyway using
>>> .shadowRoot, etc. tree walking. From that point of view it seems acceptable
>>> for offsetParent to return an offsetParent inside the (traversable) shadow.
>>>
>>
>> This seems like correct behavior. We should walk up to find a traversable
>> parent and then offsetLeft/offsetTop should be relative to those.
>>
>> (Note in webkit this is trivial since offsetLeft, offsetTop both call
>> offsetParent internally and then compute their value from it)
>>
>>
>>>
>>> On the other hand, this violates the lower-boundary encapsulation of the
>>> Shadow DOM spec. This means that pages that are using traversable shadows,
>>> but relying on convention (ie "don't use new properties like .shadowRoot")
>>> to get the encapsulation benefits of Shadow DOM, now have to audit the
>>> offsetParent property. It also means you need to have two ways of dealing
>>> with offsetParent in both user agents and author scripts. So for simplicity
>>> and consistency I think it makes sense to treat both traversable and
>>> non-traversable shadows uniformly.
>>>
>>>
>> I disagree with this
>>
>
> Which part?
>
> Returning an element inside Shadow DOM in an attribute of a node outside
> Shadow DOM violates lower boundary encapsulation.
>
>
Yes, it's sad that you can fall into a shadow "by mistake" here where all
the other APIs were designed to prevent that.


> If offsetParent returns an element inside traversable Shadow DOM, pages
> that are using traversable shadows but relying on convention to get
> encapsulation benefits will have to audit uses of the offsetParent property.
>
> If offsetParent returns an element inside traversable Shadow DOM (but not
> non-traversable Shadow DOM), there are two ways of dealing with
> offsetParent in the user agent.
>
> If offsetParent returns an element inside traversable Shadow DOM (but not
> non-traversable Shadow DOM), there are two ways of dealing with
> offsetParent in author scripts.
>

What you're proposing doesn't reduce the issues. There's still two cases,
you're just offloading all the complexity into author code by making them
walk up the tree and call getComputedStyle everywhere.


>
> It makes sense to treat both traversable and non-traversable shadows
> uniformly.
>

I disagree with this statement. By the virtue of what a non-traversable
shadow is we need to treat it special all over the place.


>
>
>> since it means offsetParent returns a nonsensical value for elements in,
>> or projected into, traversable shadow roots as it traverses all the way up
>> into the main page until it's not inside a ShadowRoot anymore.
>>
>
> In what way is that nonsensical? The return value makes sense at the level
> of abstraction the code calling innerParent is working at.
>

var rect = node.offsetParent.getBoundingClientRect();
node.style.top = computePosition(rect);
node.style.left = computePosition(rect);

Since you're walking all the way out of the shadow into the main page
you're going to get a nonsense result here. More importantly since all the
apps we've seen built using custom elements so far have <x-app> and the
entire app down there you're effectively saying that offsetParent should
return <body> for nearly every element in a Toolkit app and that the
feature becomes totally useless.


>
>
>> offsetParent is very useful to find your positioned parent, and you're
>> crippling that feature and making authors use distributedParent +
>> getComputedStyle() repeatedly which is considerably more expensive.
>>
>
> What are those use cases, except finding the position of an element
> relative to another element, which I think is not excessively complicated
> by what I am proposing here?
>

Not complicated, just very expensive. getComputedStyle allocates a new
object on every invocation and does string parsing. Unfortunately it seems
jQuery already does this:
https://github.com/jquery/jquery/blob/master/src/offset.js#L126

Anyway I think the fundamental question here is: Is it okay to make
offsetParent totally useless (always returns <body>) in Toolkit apps? Can
we add node.getPositionedAncestor(bool includingShadows) instead?

- E

Received on Thursday, 28 March 2013 16:50:33 UTC