[cssom] Element size/positioning information

The current OM APIs for determining the size and position of an
element are verbose and confusing.  There's been discussion on how to
make this better, but it's been scattered and hasn't yet produced
anything.  Chrome is interested in doing some experimental
implementation in this space, but there's a lot of possible ways to do
it, so I'd like to collect and restart the various discussions here.


Determining the Size of an Element
==================================

Right now there are four ways of asking for the width of an element:
elem.clientWidth, elem.scrollWidth,
elem.getBoundingClientRect().width, and
getComputedStyle(elem,'').width (similarly for height).

It's completely unobvious what each of these do - the only thing that
you can really gather is that .scrollWidth has something to do with
scrolling.  The term 'client' used in two of the methods is completely
opaque; even worse, the two methods don't even refer to the same
rectangle!  (In case people are unaware of what the first two actually
do, I made a diagram some time ago at
<http://www.xanthir.com/diagrams/scrollwidth-clientwidth.html>.)  The
first three return the size in pixels as an integer, while the fourth
returns size in pixels as a string with 'px' at the end.  The third is
transform-aware, while the others ignore any transforms on the
element.

Libraries like jQuery offer some simple, relatively-well-named
functions for querying various sizes that you might want.  jQuery
gives you .width() for the content box, .innerWidth() for the padding
box, and .outerWidth() for the border box.

All in all, these are the boxes that we might want to get sizing
information about:

1. content box, ignoring scrollbars
2. content box, subtracting scrollbars
3. content box, scrollable area
4. padding box (+/- scrollbars?  Depends on exactly where the impl
puts the scrollbars.)
5. border box
6. transform-aware bounding box of at least the border box, possibly other boxes


Determining the Position of an Element
======================================

Right now, there are two ways to get the position of an element:
elem.offsetTop/Left (gives you coords relative to the element's
positioning root), and
elem.getBoundingClientRect().top/right/bottom/left (post-transform
coords of the bounding box relative to the viewport).

All of the boxes listed in the above, though, can be useful to get the
dimensions of.  For example, I made a simple test page earlier today
as a precursor for a talk I'll be giving, and I used
getBoundingClientRect to help convert the mousemove event coordinates
into coordinates relative to my <canvas> element.  Unfortunately, the
rectangle returned by getBoudingClientRect is the border box, when I'd
have liked the content box - I had to manually subtract the size of
the borders from my results, which is brittle if I ever change the
width of the border in my CSS and forget to update my JS.
(Alternately, I could have gotten getComputedStyle().borderTopWidth
and .borderLeftWidth and parsed them into integers.)


Position Relative to Another Element
====================================

Right now you can determine the position of an element relative to
another element by figuring out their position relative to the same
thing manually with one of the above methods, then subtracting one's
top/left from the other.  Does this need to be made more convenient,
so you can directly ask for something's position relative to something
else?


Mouse Positioning
=================

On a closely related tangent, the mouse events expose .clientX and
.clientY properties which are relative to the viewport.  If you're
trying to find the position of the mouse relative to an element (for
example, if you're drawing something to a <canvas> where the mouse
is), you have to find the position of the element relative to the
viewport and subtract the two positions.  I think this is a good
candidate for being made easier, so you can just request the mouse
coords relative to an element.  Again, all of the boxes of an element
are potential targets for being measured relative to.


Mouse Positioning and Transfoms
===============================

When an element is being transformed, the position of the mouse
relative to it is subtler.  You may want the actual position of the
mouse relative to the current post-transform layout position of the
element - this is easy to do by just subtracting the mouse position
from the bounding-box position.  If you're drawing into a transformed
<canvas>, though, you may want the position back in pre-transform
coordinates, as if you took the stated mouse position and applied the
element's inverse transform, so you can easily draw into the canvas
and still have the visual display track the mouse.


Proposal
========

I'm not sure!  There's a lot of different possible approaches here.
I'd like to optimize for author comprehensibility, which often means
optimizing for terseness as well, but I'm not certain how best to do
that.

For the simple measurement case, something like
elem.cssBorderRect.width/height/top/right/bottom/left would work.  We
could similarly expose cssPaddingRect, cssContentRect, cssScrollRect,
etc.  Bounding boxes for transform-awareness can be requested as
cssBoundingBorderRect - in the absence of transforms, these return the
same information as their non-bounding equivalents.

Doing relative measurement is harder.  I'm not sure how I can tersely
expose that information.  It may be acceptable to just say "hey,
subtract the positions", given that getting the positions relative to
the same thing would be easier.  If anyone has better ideas, feel free
to pipe up.

The mouse positioning case has already received some attention - the
last proposal I saw was for a MouseEvent.getCoordsAt(elem) function,
which returns an object with .x and .y.  This is the point in the
transformed space (the one you can easily use to draw something under
the mouse with <canvas>).  This isn't *quite* sufficient, since it
would presumably give you coordinates relative to the border box of
the element, when you may want one of the other boxes, but it's close.

Finding the mouse position relative to an element in normal space can
probably be done by just subtracting the positions, same as finding
the relative positions of two elements.


Thoughts?

~TJ

Received on Monday, 11 April 2011 21:36:34 UTC