Bug 17152 - Support centering an element when scrolling into view.
Support centering an element when scrolling into view.
Status: NEW
Product: CSS
Classification: Unclassified
Component: CSSOM View
unspecified
All All
: P2 normal
: ---
Assigned To: Simon Pieters
public-css-bugzilla
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2012-05-22 21:47 UTC by Thaddee TYL
Modified: 2013-08-08 13:28 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thaddee TYL 2012-05-22 21:47:25 UTC
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()`.
Comment 1 Glenn Adams 2012-05-23 17:06:30 UTC
see also http://lists.w3.org/Archives/Public/www-style/2012May/0808.html
Comment 2 Thaddee TYL 2012-06-19 22:17:52 UTC
Some additional discussion occurred on the mailing-list.

As a result, the proposal has changed a little.

The WebIDL corresponding to this proposal is the following:

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

    dictionary ScrollPosition {
      float horizontal = 0.5;
      float vertical = 0.5;
      boolean evenIfViewed = false;
    };

The following paragraph is non-normative.

`horizontal` and `vertical` are meant as percentages of
the viewport width minus the width of the bounding box of the element, and
the viewport height minus the height of the bounding box of the element,
respectively.

(Having a boolean flag does what is already specified.)

Having a dictionary as a parameter runs the following algorithm:

1. If the Document associated with the element to be scrolled into view is not same origin with the Document associated with the element or viewport associated with the scrolling box, terminate these steps.

2. If `evenIfViewed` is false:
- If the bounding box of the element this method is applied to is completely out of the viewport, scroll the element to ((innerWidth of the window - offsetWidth of the element) * horizontal ; (innerHeight of the window - offsetHeight of the element) * vertical).
- Else if the element is partially visible:
 * If the lower part of the bounding box of the element this method is applied to is partially visible, align the top of the border box of the element to be scrolled into view with the top of the scrolling box,
 * If the higher part of the bounding box of the element this method is applied to is partially visible, align the bottom of the border box of the element to be scrolled into view with the bottom of the scrolling box,
 * If the right part of the bounding box of the element this method is applied to is partially visible, align the left of the border box of the element to be scrolled into view with the left of the scrolling box,
 * If the left part of the bounding box of the element this method is applied to is partially visible, align the right of the border box of the element to be scrolled into view with the right of the scrolling box,

3. If `evenIfViewed` is true, scroll the element to ((innerWidth of the window - offsetWidth of the element) * horizontal ; (innerHeight of the window - offsetHeight of the element) * vertical).

4. If scrolling the element caused content to move queue a task to fire an event named scroll at the element, unless a task to fire that event at that element was already queued.

Finally, a polyfill for this proposal has been implemented, and is available for public consumption at <http://jsbin.com/3/ilecok/13/edit?javascript>.


Robert O'Callahan raised concerns related to CSS-regions, which may cause elements to be fragmented between several scrollable containers. The specification I give above depends on the concept of "scrolling an element", which should be changed anyway to accommodate this possibility.
Comment 3 Glenn Adams 2012-08-03 05:55:17 UTC
reassign to myself; apparently i am not the default assignee for this component
Comment 4 Simon Pieters 2013-05-14 16:00:33 UTC
(In reply to comment #0)
> As a result, what authors really want is to center
> the element.

If that's what authors want, why does the proposal do more than that? What's the use case for arbitrary positioning? What's the use case for `evenIfViewed`?
Comment 5 Thaddee TYL 2013-05-14 20:00:36 UTC
(In reply to comment #4)
> (In reply to comment #0)
> > As a result, what authors really want is to center
> > the element.
> 
> If that's what authors want, why does the proposal do more than that? What's
> the use case for arbitrary positioning? What's the use case for
> `evenIfViewed`?

There are two reasons to use this.

One is to perform search. In that case, preserving the surroundings of the element that currently matched (in a series of matches) allows the user to see in which context the element is. A common UI decision in this case is to highlight the element in some way. As a result, it is not necessary to actually bring the element at the center of the viewport; furthermore, not changing the viewport allows the user to see the spatial relationship between the element that currently match with the next one in the list. In this case, the author will probably want to set `evenIfViewed` to false.

The other one is simply to bring an element into the viewport. In that case, there is no list of matches, we simply want to force a certain element into the viewport in a specific way. The author will then want to set `evenIfViewed` to true.

In that latter case, the author may also want to position the element in a different way than simply centering the element in the viewport. For instance, if the bottom of the element is what is relevant for the user, it must be shown. If the element is too big vertically for the viewport, the author may want to set `vertical` to 1, to force the bottom of the element into view.
Comment 6 Simon Pieters 2013-06-18 08:55:42 UTC
The 'top' argument is now aware of vertical writing modes. I think it would make sense for these settings to be aware of vertical writing modes, too (otherwise it wouldn't be possible to specify the equivalent of "top" and also set eventIfViewed without having to check what the writing mode is).

The question is whether the names vertical and horizontal make sense with that in mind. The writing-mode aware spec terms are "inline base direction" and "block flow direction", but I don't think such names are exposed to Web authors currently.