[CSSOM-view] Support centering an element when scrolling into view.

A common pattern in the web is to use `element.scrollIntoView()` to
show that element
in the context of the web page. However, the design of scrollIntoView
puts that element
either at the very top or at the very bottom of the viewport.

In most cases, web authors want to center that element. As a result,
there is great duplication
of code to make that work, and scrollIntoView gets rarely used, except
for quick and dirty
implementation of a search functionality.

I suggest a solution to that in the following bug, copy and pasted below.

All feedback is warmly welcome!

https://www.w3.org/Bugs/Public/show_bug.cgi?id=17152


---

The main reason why we might use `scrollIntoView()` is to bring an
element into the viewport.

However, showing that element at the top or at the bottom of the
viewport is detrimental to understanding the context in which this
element is. As a result, what authors really want is to center
the element.

I know we are implementing that in JS again and again throughout Firefox,
and web developers are, too.


Webkit has a method called
`void scrollIntoViewIfNeeded(optional boolean center);`,
which scrolls the page so that an element, if not completely shown,
becomes completely shown, and appears vertically centered in the viewport
if the boolean is true.

The reason I bring this method up is to highlight its bad design.

1. Boolean flags are unreadable: it is unclear from reading
   `scrollIntoViewIfNeeded(false)` what that `false` means, and even if
   we know that it is about centering the element, which one, of `true`
   and `false`, does indeed center the element.

2. We already have `void scrollIntoView(optional boolean top)`.
   Having many methods whose intents are collinear is wrong.
   Different methods should have orthogonal goals.

I therefore suggest the following addition:

    partial interface Element {
      void scrollIntoView(ScrollPosition options);
    };

    dictionary ScrollPosition {
      float top = 0.5;
      float left = 0.0;
      boolean notIfViewed = true;
    };

where `top` and `left` are meant as percentages of
`max(0, scrollY - element.clientHeight)` and
`max(0, scrollX - element.clientWidth)` respectively.

If `notIfViewed` is true, and the element is completely in the viewport,
then this method does nothing.
If `notIfViewed` is true, and the element is partially hidden,
then scroll just enough so that the element appears completely
in the viewport.

In all other cases, scroll so that the element is positioned at
`options.top * max(0, scrollY - element.clientHeight)` with respect
to the top border edge, and at
`options.left * max(0, scrollX - element.clientWidth)` with respect
to the left border edge.

Overloading of `scrollIntoView` happens
as described at <http://www.w3.org/TR/WebIDL/#idl-overloading>.

An intended effect of this design is that calling
`element.scrollIntoView({});` automatically does the right thing and
centers the element on the screen, while you can still get the
old behavior by calling `element.scrollIntoView()`.

Received on Tuesday, 22 May 2012 22:18:02 UTC