Bug 15430 - Interaction with getBoundingClientRect/elementFromPoint is not defined
Interaction with getBoundingClientRect/elementFromPoint is not defined
Status: RESOLVED FIXED
Product: CSS
Classification: Unclassified
Component: CSSOM
unspecified
All All
: P2 normal
: ---
Assigned To: Simon Pieters
public-css-bugzilla
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2012-01-05 20:00 UTC by Aryeh Gregor
Modified: 2013-09-30 14:02 UTC (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Aryeh Gregor 2012-01-05 20:00:53 UTC
If a box is transformed, what values are reported for various CSSOM View features?  Specifically: getClientRects(), getBoundingClientRect(), clientTop, clientLeft, clientWidth, clientHeight, offsetTop, offsetLeft, offsetWidth, and offsetHeight.  CSSOM View assumes that everything is an axis-aligned rectangle, but transformed boxes will be parallelograms in general.

Using the Live DOM Viewer <http://software.hixie.ch/utilities/js/live-dom-viewer/#> with this test page:

----
<!DOCTYPE html>
<style>
body { margin: 0 }
div {
transform: rotate(45deg);
-ms-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
-o-transform: rotate(45deg);
height: 50px;
width: 50px;
background: blue;
}
</style>
<div></div>
<script>
var div = document.querySelector("div");
w(div.getClientRects()[0].height);
w(div.getBoundingClientRect().height);
w(div.clientTop);
w(div.clientLeft);
w(div.clientWidth);
w(div.clientHeight);
w(div.offsetTop);
w(div.offsetLeft);
w(div.offsetWidth);
w(div.offsetHeight);
</script>
----

I find that IE9, Firefox 12.0a1, Chrome 17 dev, and Opera Next 12.00 alpha all behave similarly: getClientRects() and getBoundingClientRect() reflects the transformed box (height is about 71 or 72 pixels), while client* and offset* reflect the untransformed box (0 and 50 pixels).  This should be standardized somewhere, probably in Transforms but perhaps in CSSOM View.
Comment 1 Aryeh Gregor 2012-01-09 15:03:47 UTC
roc thinks this should be covered by CSSOM View, not Transforms:

http://lists.w3.org/Archives/Public/www-style/2012Jan/0325.html

There seems to be no Bugzilla component for CSSOM View, though, so I'll leave the bug here for now.
Comment 2 Aryeh Gregor 2012-01-31 19:39:31 UTC
scrollIntoView() should also handle transforms:

https://bugzilla.mozilla.org/show_bug.cgi?id=703241
Comment 3 Aryeh Gregor 2012-01-31 20:05:52 UTC
Also needs thinking about: elementFromPoint, event.offsetX/offsetY, probably lots of other things . . .

https://bugs.webkit.org/show_bug.cgi?id=21870
Comment 4 Aryeh Gregor 2012-02-22 20:03:45 UTC
I propose that the specification say for now that:

* getBoundingClientRect() on both Element and Range must return the smallest rectangle that bound the transformed box.  If the transform is singular, the box will get mapped to a point or line segment, but this method must still return the smallest rectangle that contains that point or line segment.
* getClientRects() behaves the same.
* The HTMLElement properties clientTop, clientLeft, clientWidth, clientHeight, offsetTop, offsetLeft, offsetWidth, and offsetHeight all ignore transforms.  (This matches all browsers I tested in.  I'm not totally sure whether it's a good idea for offset*, but we should spec it unless implementations want to change.)
* Hit-testing must respect transforms, as must elementFromPoint() and caretPositionFromPoint().
* The MouseEvent properties offsetX and offsetY must give the untransformed offset.  So if you have a 100px wide square that's scaled to only 50px, clicking at the right should give offsetX of 100, not 50.  This matches all browsers that implement offsetX/offsetY (Firefox doesn't).
* scrollIntoView() on a transformed element must scroll the transformed border box into view.  IE and WebKit get this right, Gecko and Opera get it wrong.  If the transform is singular, it still must scroll the topmost or bottommost point of the transformed box into view.

If there are no objections within at least two full workdays, I'll make the changes, as we agreed.
Comment 5 Simon Fraser 2012-02-22 20:08:27 UTC
Sounds fine to me.
Comment 6 Edward O'Connor 2012-02-22 20:50:35 UTC
Sounds reasonable to me as well.
Comment 7 Dirk Schulze 2012-02-23 05:42:04 UTC
IMO this is something that should go to CSSOM View Module[1]. For the moment I am fine with defining it in CSS Transforms. But we should file a request to add the text into the current WD of CSSOM View Module. Or suggest the wording and ask the editors to add it.

If I understand it correctly, getBoundingClientRect() should cover the transformation as well? So should a CSSRect always be relative to the coordinate space of the top root element (means you get a rect which sides are parallel to the window border)?

+-----+
|    /\    |
|  /    \  |
|/        \|
|\        /|
|  \    /  |
|    \/    |
+-----+

So rhombus is a rotated div, the outer box is the bounding rect?

How about this example

<div style="transform: rotate(22.5deg)">
  <div style="transform: rotate(22.5deg)">
  </div>
</div>

How does the bounding rect look for the second (the nested) div box? Rotated by 22.5 degree, or in absolute coordinates?

Just to make sure. I want to know if the bounding rect is always in absolute coordinates, or in local coordinates but with the current transform applied.

What if you embed an iframe which gets transformed. The iframe contains other elements that are transformed as well. Is the bounding box in the coordinates of the iframe or in the coordinates of the top root element again?


Please note, that you can not change the behavior of getBoundingClientRect() and getClientRects() for SVG elements without an associated CSS layout box. Just to make sure what it means:

<div>
  <svg>
    <rect />
    <svg>
      <rect/>
    </svg>
  </svg>
</div>

The first SVG element (the direct child of the div box) is an SVG element with an associated CSS layout box. You can set margin, padding, border and so on. The other elements are not.

[1] http://www.w3.org/TR/cssom-view/
Comment 8 Glenn Adams 2012-02-23 05:48:18 UTC
(In reply to comment #7)
> IMO this is something that should go to CSSOM View Module[1]. For the moment I
> am fine with defining it in CSS Transforms. But we should file a request to add
> the text into the current WD of CSSOM View Module. Or suggest the wording and
> ask the editors to add it.
> 
> [1] http://www.w3.org/TR/cssom-view/

agreed; cssom-view is in process of being updated now, so i can fold this change in for the next ED
Comment 9 Simon Fraser 2012-02-23 15:39:07 UTC
(In reply to comment #7)
> IMO this is something that should go to CSSOM View Module[1]. For the moment I
> am fine with defining it in CSS Transforms. But we should file a request to add
> the text into the current WD of CSSOM View Module. Or suggest the wording and
> ask the editors to add it.

I would be fine with this too, and think that it's a better solution; it would be a little
odd for CSS Transforms to talk about CSS OM View interfaces, since no other CSS
spec does this AFAIK.

> If I understand it correctly, getBoundingClientRect() should cover the
> transformation as well? So should a CSSRect always be relative to the
> coordinate space of the top root element (means you get a rect which sides are
> parallel to the window border)?
> 
> +-----+
> |    /\    |
> |  /    \  |
> |/        \|
> |\        /|
> |  \    /  |
> |    \/    |
> +-----+
> 
> So rhombus is a rotated div, the outer box is the bounding rect?

Yes.

> How about this example
> 
> <div style="transform: rotate(22.5deg)">
>   <div style="transform: rotate(22.5deg)">
>   </div>
> </div>
> 
> How does the bounding rect look for the second (the nested) div box? Rotated by
> 22.5 degree, or in absolute coordinates?
> 
> Just to make sure. I want to know if the bounding rect is always in absolute
> coordinates, or in local coordinates but with the current transform applied.

The client rect is obtained by mapping the 4 corners of the target element
into viewport coordinates, and then taking a bounding rect around those 4 points.
So the client rect is always viewport-aligned. This is true even for 3d-transformed elements.

> What if you embed an iframe which gets transformed. The iframe contains other
> elements that are transformed as well. Is the bounding box in the coordinates
> of the iframe or in the coordinates of the top root element again?

Whatever getBoundingClientRect normally does, which I presume is return
a rect relative to the viewport of the window on which it is being called.

> Please note, that you can not change the behavior of getBoundingClientRect()
> and getClientRects() for SVG elements without an associated CSS layout box.
> Just to make sure what it means:
> 
> <div>
>   <svg>
>     <rect />
>     <svg>
>       <rect/>
>     </svg>
>   </svg>
> </div>
> 
> The first SVG element (the direct child of the div box) is an SVG element with
> an associated CSS layout box. You can set margin, padding, border and so on.
> The other elements are not.

I'm not sure what you mean here.
Comment 10 Aryeh Gregor 2012-02-23 19:12:58 UTC
(In reply to comment #7)
> IMO this is something that should go to CSSOM View Module[1]. For the moment I
> am fine with defining it in CSS Transforms. But we should file a request to add
> the text into the current WD of CSSOM View Module. Or suggest the wording and
> ask the editors to add it.

I think it belongs in CSSOM View ultimately, but it can live in Transforms for now if we can make the change there sooner.  If Glenn is willing to add it to CSSOM View soon, it doesn't need to go into Transforms.

> If I understand it correctly, getBoundingClientRect() should cover the
> transformation as well? So should a CSSRect always be relative to the
> coordinate space of the top root element (means you get a rect which sides are
> parallel to the window border)?

Right.

> Just to make sure. I want to know if the bounding rect is always in absolute
> coordinates, or in local coordinates but with the current transform applied.

Always relative to the viewport, in its coordinates.

> What if you embed an iframe which gets transformed. The iframe contains other
> elements that are transformed as well. Is the bounding box in the coordinates
> of the iframe or in the coordinates of the top root element again?

The coordinates of whatever the window is for the element you call it on.  So the iframe's window, if it's in an iframe.  Methods on things in iframes don't know they're in an iframe.  This shouldn't have to do with transforms.

> Please note, that you can not change the behavior of getBoundingClientRect()
> and getClientRects() for SVG elements without an associated CSS layout box.
> Just to make sure what it means:
> 
> <div>
>   <svg>
>     <rect />
>     <svg>
>       <rect/>
>     </svg>
>   </svg>
> </div>
> 
> The first SVG element (the direct child of the div box) is an SVG element with
> an associated CSS layout box. You can set margin, padding, border and so on.
> The other elements are not.

Okay, so this should only be for things with a CSS layout box.  Fine by me.
Comment 11 Simon Pieters 2013-09-19 13:39:01 UTC
(In reply to Aryeh Gregor from comment #4)
> * getBoundingClientRect() on both Element and Range must return the smallest
> rectangle that bound the transformed box.  If the transform is singular, the
> box will get mapped to a point or line segment, but this method must still
> return the smallest rectangle that contains that point or line segment.
> * getClientRects() behaves the same.

I think the spec does this now. Please verify.

https://dvcs.w3.org/hg/csswg/rev/d6d6ed08b91a

> * The HTMLElement properties clientTop, clientLeft, clientWidth,
> clientHeight, offsetTop, offsetLeft, offsetWidth, and offsetHeight all
> ignore transforms.  (This matches all browsers I tested in.  I'm not totally
> sure whether it's a good idea for offset*, but we should spec it unless
> implementations want to change.)

Done.

https://dvcs.w3.org/hg/csswg/rev/6091febd3717

It seems that in Gecko, offsetParent can be an ancestor that has a transform set. Blink/WebKit/IE9 don't do that.

http://software.hixie.ch/utilities/js/live-dom-viewer/saved/2535
Comment 12 Simon Pieters 2013-09-19 14:04:22 UTC
(In reply to Aryeh Gregor from comment #4)
> * Hit-testing must respect transforms, as must elementFromPoint() and
> caretPositionFromPoint().

http://software.hixie.ch/utilities/js/live-dom-viewer/saved/2536

We don't have a spec for hit testing, but I fixed elementFromPoint, elementsFromPoint and caretPositionFromPoint.

https://dvcs.w3.org/hg/csswg/rev/0e33c7674d1e
Comment 13 Simon Pieters 2013-09-19 14:11:56 UTC
(In reply to Aryeh Gregor from comment #4)
> * The MouseEvent properties offsetX and offsetY must give the untransformed
> offset.  So if you have a 100px wide square that's scaled to only 50px,
> clicking at the right should give offsetX of 100, not 50.  This matches all
> browsers that implement offsetX/offsetY (Firefox doesn't).

http://software.hixie.ch/utilities/js/live-dom-viewer/saved/2537

Fixed.

https://dvcs.w3.org/hg/csswg/rev/2623f63c16ee
Comment 14 Simon Pieters 2013-09-19 14:25:47 UTC
(In reply to Aryeh Gregor from comment #4)
> * scrollIntoView() on a transformed element must scroll the transformed
> border box into view.  IE and WebKit get this right, Gecko and Opera get it
> wrong.  If the transform is singular, it still must scroll the topmost or
> bottommost point of the transformed box into view.

Can you elaborate on what you think is right here?

In http://software.hixie.ch/utilities/js/live-dom-viewer/saved/2538 Gecko seems to do what you want (the topmost point of the transformed box is scrolled into view), while Bilnk does the opposite (the pre-transform "top" border edge is scrolled into view).
Comment 15 Simon Fraser 2013-09-19 15:13:11 UTC
(In reply to Simon Pieters from comment #14)
> (In reply to Aryeh Gregor from comment #4)
> > * scrollIntoView() on a transformed element must scroll the transformed
> > border box into view.  IE and WebKit get this right, Gecko and Opera get it
> > wrong.  If the transform is singular, it still must scroll the topmost or
> > bottommost point of the transformed box into view.
> 
> Can you elaborate on what you think is right here?
> 
> In http://software.hixie.ch/utilities/js/live-dom-viewer/saved/2538 Gecko
> seems to do what you want (the topmost point of the transformed box is
> scrolled into view), while Bilnk does the opposite (the pre-transform "top"
> border edge is scrolled into view).

I think the correct behavior is to take the transforms into account for scrollIntoView() (seems like Blink is wrong).
Comment 16 Simon Pieters 2013-09-20 11:43:23 UTC
OK. The spec already does that now with the change to getBoundingClientRect(), AFAICT.

Please reopen or file new bugs if there are cases not covered by the spec.
Comment 17 Aryeh Gregor 2013-09-30 14:02:14 UTC
At a glance, LGTM -- thanks!