Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
On Mon, Mar 25, 2013 at 2:48 AM, Dominic Cooney domin...@chromium.orgwrote: On Sun, Mar 24, 2013 at 3:50 PM, Elliott Sprehn espr...@gmail.com wrote: On Mon, Mar 18, 2013 at 4:48 AM, Dominic Cooney domin...@chromium.orgwrote: ... 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:
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
On Wed, Mar 27, 2013 at 2:02 PM, Boris Zbarsky bzbar...@mit.edu wrote: Scott Miles wrote: This is a thorny problem, but my initial reaction is that you threaded the needle appropriately. I don't see how we avoid some lossiness in this situation. Note that if you're using offsetWith/Height/Top/Bottom you already lose, because they return integers. I think we should be doing what we can to discourage use of these broken APIs, for what it's worth, instead of worrying how we can extend their already-incorrect behavior to cover more cases well. That's fair, how do you feel about getPositionedAncestor(bool includingTraversableShadows) ? This would solve the primary use cases for offsetParent, and also mean jQuery wouldn't need to call getComputedStyle all the way up the tree. Walking the box tree is super fast in C++, and isPositioned is a bitfield check. Doing this in JS with getComputedStyle is quite a lot more expensive (allocate CSSComputedStyleDeclaration, parse the property name, return the position value, compare the string). - E
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
On Thu, Mar 28, 2013 at 12:49 PM, Elliott Sprehn espr...@gmail.com wrote: On Mon, Mar 25, 2013 at 2:48 AM, Dominic Cooney domin...@chromium.orgwrote: On Sun, Mar 24, 2013 at 3:50 PM, Elliott Sprehn espr...@gmail.comwrote: 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 I read this last sentence as The most popular JavaScript library doesn't suffer from any problems with this proposal. This is unfortunate. I'm sure you meant that it's unfortunate that we take the slow path, but as Boris has already stated Note that if you're using offsetWith/Height/Top/Left you already lose, because they return integers. We tried to ignore this problem for years. Unfortunately, he's right and using offset* will eventually sneak up and cause seemingly inexplicable bugs. Also, considering that jQuery is already doing this, it seems clear that this isn't a performance bottleneck. FWIW, I was going to make the same exact argument that this proposal was going to break code for positioning elements relative to other elements, which is something we do dynamically all the time in jQuery UI. But then I looked at the code in jQuery core and asked Mike Sherov if he could provide feedback. I think he never did because there just doesn't seem to be a good response. Coincidentally, a few days later, Mike and I were reviewing some completely unrelated code and spent at least half an hour trying to figure out why some elements were being positioned 1px off from their expected location and it came down to offset* being lossy.
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
On 3/28/13 12:55 PM, Elliott Sprehn wrote: and isPositioned is a bitfield check That happens to be a WebKit-specific claim, as far as I can tell. parse the property name As is this. But yes, the general claim that the JS APIs for doing this right now are full of performance fail stands. ;) -Boris
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
Scott Miles wrote: This is a thorny problem, but my initial reaction is that you threaded the needle appropriately. I don't see how we avoid some lossiness in this situation. Note that if you're using offsetWith/Height/Top/Bottom you already lose, because they return integers. I think we should be doing what we can to discourage use of these broken APIs, for what it's worth, instead of worrying how we can extend their already-incorrect behavior to cover more cases well. -Boris
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
On 3/27/13 2:02 PM, Boris Zbarsky wrote: Note that if you're using offsetWith/Height/Top/Bottom you already lose, s/Bottom/Left/, of course. -Boris
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
On Sun, Mar 24, 2013 at 3:50 PM, Elliott Sprehn espr...@gmail.com wrote: On Mon, Mar 18, 2013 at 4:48 AM, Dominic Cooney domin...@chromium.orgwrote: ... 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. 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. It makes sense to treat both traversable and non-traversable shadows uniformly. 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. 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? Dominic
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
On Mon, Mar 18, 2013 at 4:48 AM, Dominic Cooney domin...@chromium.orgwrote: ... 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 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. 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. - E
Re: [webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
Sorry for the late response, this is one of those bad cases where agreement was expressed as silence. This is a thorny problem, but my initial reaction is that you threaded the needle appropriately. I don't see how we avoid some lossiness in this situation. Scott On Mon, Mar 18, 2013 at 1:48 AM, Dominic Cooney domin...@chromium.orgwrote: Summary: I think the Shadow DOM spec should specify how offset* properties are handled around shadows. Further, I think traversable and non-traversable shadows should be handled uniformly. The offsetParent property should return the first offsetParent at the same level of shadow as the receiver, or the document, to maintain lower-boundary encapsulation. And the offset{Top, Left} properties should be accumulated across skipped offsetParents. Problem: It seems the consensus is that there will be two kinds of shadows, ones that are exposed to the page through properties such as HTMLElement.shadowRoot, and ones that aren't [1]. The language is emerging but for now I will refer to these as traversable and non-traversable shadows respectively. In both cases, there's a question of how to handle HTMLElement.offset* properties, particularly offsetParent. [2] Let's talk about a specific example: div id=a div id=b {#a's ShadowRoot} div id=c style=position: relative; left: 10px; div id=d content In this case, the positioned ancestor of #b is #c. What should the result of b.offsetParent be? If the ShadowRoot is not traversable it is clear that b.offsetParent should NOT be c. If it were, it would be very difficult to use not-traversable shadows that don't accidentally leak an internal node. (Especially when you consider that c could be a pseudo-element, and the author could set position: relative on the element that way.) Discussion: 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. 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. Dominic [1] Thread starts here: http://lists.w3.org/Archives/Public/public-webapps/2013JanMar/0535.html [2] http://www.w3.org/TR/cssom-view/#offset-attributes http://goto.google.com/dc-email-sla
[webcomponents] Adjusting offsetParent, offsetTop, offsetLeft properties in Shadow DOM
Summary: I think the Shadow DOM spec should specify how offset* properties are handled around shadows. Further, I think traversable and non-traversable shadows should be handled uniformly. The offsetParent property should return the first offsetParent at the same level of shadow as the receiver, or the document, to maintain lower-boundary encapsulation. And the offset{Top, Left} properties should be accumulated across skipped offsetParents. Problem: It seems the consensus is that there will be two kinds of shadows, ones that are exposed to the page through properties such as HTMLElement.shadowRoot, and ones that aren't [1]. The language is emerging but for now I will refer to these as traversable and non-traversable shadows respectively. In both cases, there's a question of how to handle HTMLElement.offset* properties, particularly offsetParent. [2] Let's talk about a specific example: div id=a div id=b {#a's ShadowRoot} div id=c style=position: relative; left: 10px; div id=d content In this case, the positioned ancestor of #b is #c. What should the result of b.offsetParent be? If the ShadowRoot is not traversable it is clear that b.offsetParent should NOT be c. If it were, it would be very difficult to use not-traversable shadows that don't accidentally leak an internal node. (Especially when you consider that c could be a pseudo-element, and the author could set position: relative on the element that way.) Discussion: 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. 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. Dominic [1] Thread starts here: http://lists.w3.org/Archives/Public/public-webapps/2013JanMar/0535.html [2] http://www.w3.org/TR/cssom-view/#offset-attributes http://goto.google.com/dc-email-sla