w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 668 forks source link

[cssom-view] getBoundingClientRect undefined in paged media #4463

Open frivoal opened 5 years ago

frivoal commented 5 years ago

There are a few steps to determining the return value of getBoundingClientRect(). The base/fallback case is described as:

return a DOMRect object describing the smallest rectangle that includes all of the rectangles in list of which the height or width is not zero.

Each rectangle in the list corresponds to a single fragment, so far so good.

However, while it is true on screen, in the general case, there is no guarantee that all these per-fragment-rectangles exist in a singe coordinate space. In paginated media, each page is a 2d space, and you can reason about "the smallest rectangle that includes all of the rectangles" within that page. However, if two of these rectangles end up being on different pages, "the smallest rectangle that includes both" is undefined, because there is no well defined 2d space in which the pages themselves are placed. Since some of the environments in which pagination is possible also support scripting, this needs an answer.

Possible answers (with no particular judgement as to which is a good idea):

  1. define this abstract space in which the pages are placed, so that when some object is on page 2, we can express where it is in terms of the coordinate system rooted in page 1. Maybe they're laid out next to each other horizontally from left to right, top aligned, with their bleed edges adjacent. Or maybe their margin edge. Or maybe they're stacked top to bottom, left aligned. Maybe there's a notion of spread, and pages in a spread are next to each other horizontally, and spreads are stacked up vertically. Maybe the author has control about whether there are spreads or not. Maybe this is sensitive to writing-modes and direction, maybe not.
  2. define that getBoundingClientRect() does not consider the whole list, but only the rectangles within the list that fit within the same page as the first one in the list / or within the first page in the document.
  3. Same as 2, but abstracted to any kind of fragmentation container, not just pages.
  4. return undefined
  5. throw an exception
  6. return a list/array/collection of some kind, defining each such smallest rectangle per page.

If we had a time machine, I'd advocate for 6, but we don't. All the other options seem somewhat unappealing to me for various reasons, yet we still need to know what this API is supposed to return.

frivoal commented 5 years ago

Just thought of another alternative. This one seems bad too as it is largely meaningless, but as a logical possibility that might be the result of naive implementation, I though it was worth listing:

  1. Ignore the fact that pages are different coordinate spaces, as calculate the bounding rectangles as if the pages were superposed: the intersection of {x: 0, y: 0, width: 10, height: 10} on page 1 and {x: 5, y: 10, width: 10, height: 10} on page 2 would be {x: 0, y: 0, width: 15, height: 20}
emilio commented 5 years ago

return a list/array/collection of some kind, defining each such smallest rectangle per page.

So like getClientRects()?

frivoal commented 5 years ago

Well, not quite. getClientRects() gives you one rectangle per fragment of the element. The goal of getBoundingClientRect() is to get a bounding box grouping those. But you can only group per page.

So if you have an element with 5 fragments, 2 on the first page and 3 on the second, getClientRects() will give you 5 rectangles. getBoundingClientRect() as currently specified will give you one, but what it is is undefined. Some kind of getBoundingClientRects() (note the plural) would give you 2: one that is the bounding box of both fragments on the first page, one that is the bounding box of the 3 fragments on the second page.