Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cssom-view-1] Update spec text for visual viewport scroll and scrollend events #8205

Merged
merged 6 commits into from
Apr 24, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 142 additions & 23 deletions cssom-view-1/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,71 @@ does not cause reflow of the page's contents.

The magnitude of the scale transform is known as the <a>visual viewport</a>'s <dfn>scale factor</dfn>.

<style>
@keyframes lvmove {
0%, 10%, 20% { transform: translate(0, 100px); }
30%, 50%, 70% { transform: translate(0, 150px); }
80%,100% { transform: translate(0, 100px); }
}
@keyframes vvmove {
0%, 10% { transform: translate(70px, 150px); }
30%, 50% { transform: translate(70px, 250px); }
80%, 90% { transform: translate(70px, 100px); }
100% { transform: translate(70px, 150px); }
}
#lv {
transform: translate(0, 100px);
animation: lvmove linear 3s infinite;
}
#vv {
transform: translate(20px, 150px);
animation: vvmove linear 3s infinite;
}
.paused #lv {
animation-play-state: paused;
}
.paused #vv {
animation-play-state: paused;
}
</style>
<div class="example paused" id="example-vvanimation">
<p>
This animation shows an example of a zoomed in visual viewport being "panned" around (for
example, by a user performing a touch drag). The page is scaled so that the layout viewport
is larger than the visual viewport.
</p>
<p>
A scroll delta is applied to the visual viewport first. When the visual viewport is at its
extent, scroll delta will be applied to the layout viewport. This behavior is implemented by
the [=viewport/perform a scroll=] steps.
</p>
<svg width="300" height="400">
<rect width="100%" height="100%" fill="white" stroke="grey" stroke-width="3"/>
<text x="10" y="30" font-family="Sans" font-size="25" fill="grey">
Document
</text>
<g id="lv">
<rect y="2" x="2" width="296" height="200" stroke="blue" fill="none" stroke-width="4"/>
<text x="10" y="20" font-family="Sans" font-size="15" fill="blue">
Layout Viewport
</text>
</g>
<g id="vv">
<rect y="2" x="2" width="148" height="100" stroke="red" fill="none" stroke-width="4"/>
<text x="10" y="60" font-family="Sans" font-size="15" fill="red">
Visual Viewport
</text>
</g>
</svg>
<br>
<button id="vvanimationBtn">Toggle Animation</button>
<script>
document.getElementById('vvanimationBtn').onclick = () => {
document.getElementById('example-vvanimation').classList.toggle('paused');
};
</script>
</div>

The {{VisualViewport}} object has an <dfn for=visualviewport>associated document</dfn>, which is a {{Document}} object.
It is the <a for="/">associated document</a> of the owner {{Window}} of {{VisualViewport}}. The <a>layout viewport</a>
is the owner {{Window}}'s <a>viewport</a>.
Expand Down Expand Up @@ -363,10 +428,50 @@ the following steps must be run:
Note: <code>behavior: "instant"</code> always performs an <a>instant scroll</a> by this algorithm.

Note: If the scroll position did not change as a result of the user interaction or programmatic invocation, where no translations were applied as a result, then no <a event>scrollend</a> event fires because no scrolling occured.

Note: In the case the VisualViewport was scrolled, yielding no translation changes in the document, emit the <a event>scrollend</a> on the VisualViewport just as the <a event>scroll</a> event is emitted for VisualViewport scrolling.
</ol>

When a user agent is to <dfn for="viewport" export>perform a scroll</dfn> of a <a>viewport</a> to a given position <var>position</var> and optionally a scroll behavior <var>behavior</var>
(which is "<code>auto</code>" if omitted) it must perform a coordinated viewport scroll by following these steps:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "coordinated" here imply?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in: we're coordinating a scroll distributed between the visual viewport's scrolling box and the document's viewport's (i.e. "layout viewport") scrolling box.

Basically "perform a scroll" for a scrolling box follows the previously defined steps while "perform a scroll" for a viewport is "overloaded" to perform this "coordination" by splitting up the scroll and calling "perform a scroll" on each of the viewport's scrolling boxes.

If "coordinated" isn't adding anything (other than confusion) I'm happy to remove it. Or perhaps give these steps a separate name (e.g. "perform a viewport scroll")?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smfr ping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smfr PTAL

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meta comment: I think it might be clearer to refer to a “pan” when moving the visual viewport, and a “scroll” when moving the layout viewport (even though this text talks about firing a “scroll” event on the visual viewport).

When the visual viewport moves without a corresponding layout viewport change, the scrollbar doesn’t change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, how would that work in terms of spec text? We're calling the "perform a scroll" steps in both cases. I could add a "perform a pan" but it would just call "perform a scroll" which might be confusing?

FWIW - in Chrome the visual viewport does affect the scrollbar. Zooming in shrinks the scrollbar and panning the visual viewport does move the scrollbars.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Just mentioning for completeness, Firefox's scrollbars also reflect the visual viewport, i.e. shrink when zooming in and move when the visual viewport is scrolled.)


1. Let <var>doc</var> be the <a>viewport's</a> associated {{Document}}.
1. Let <var>vv</var> be the {{VisualViewport}} whose <a for=visualviewport>associated document</a> is <var>doc</var>.
1. Let <var>maxX</var> be the difference between <a>viewport</a>'s <a>scrolling box</a>'s width and the value of <var>vv</var>'s <a attribute
for=VisualViewport lt=width>width</a> attribute.
1. Let <var>maxY</var> be the difference between <a>viewport</a>'s <a>scrolling box</a>'s height and the value of <var>vv</var>'s <a attribute
for=VisualViewport lt=height>height</a> attribute.
1. Let <var>dx</var> be the horizontal component of <var>position</var> - the value <var>vv</var>'s <a attribute for=VisualViewport lt=pageLeft>pageLeft</a> attribute
1. Let <var>dy</var> be the vertical component of <var>position</var> - the value of <var>vv</var>'s <a attribute for=VisualViewport lt=pageTop>pageTop</a> attribute
1. Let <var>visual x</var> be the value of <var>vv</var>'s <a attribute for=VisualViewport lt=offsetLeft>offsetLeft</a> attribute.
1. Let <var>visual y</var> be the value of <var>vv</var>'s <a attribute for=VisualViewport lt=offsetTop>offsetTop</a> attribute.
1. Let <var>visual dx</var> be min(<var>maxX</var>, max(0, <var>visual x</var> + <var>dx</var>)) - <var>visual x</var>.
1. Let <var>visual dy</var> be min(<var>maxY</var>, max(0, <var>visual y</var> + <var>dy</var>)) - <var>visual y</var>.
1. Let <var>layout dx</var> be <var>dx</var> - <var>visual dx</var>
1. Let <var>layout dy</var> be <var>dy</var> - <var>visual dy</var>
1. Let <var>element</var> be <var>doc</var>'s root element if there is one, null otherwise.
1. <a for="/">Perform a scroll</a> of the <a>viewport</a>'s <a>scrolling box</a> to its current scroll position + (<var>layout dx</var>, <var>layout dy</var>) with <var>element</var> as the
associated element, and <var>behavior</var> as the scroll behavior.
1. <a for="/">Perform a scroll</a> of <var>vv</var>'s <a>scrolling box</a> to its current scroll position + (<var>visual dx</var>, <var>visual dy</var>) with <var>element</var> as the associated
element, and <var>behavior</var> as the scroll behavior.

Note: Conceptually, the visual viewport is scrolled until it "bumps up" against the layout viewport
edge and then "pushes" the layout viewport by applying the scroll delta to the layout viewport.
However, the scrolls in the steps above are computed ahead of time and applied in the opposite order
so that the layout viewport is scrolled before the visual viewport. This is done for historical
reasons to ensure consistent scroll event ordering. See the <a href="#example-vvanimation">example
above</a> for a visual depiction.

<div class='example'>
The user pinch-zooms into the document and ticks their mouse wheel, requesting the user agent scroll the document down by 50px. Because the document
is pinch-zoomed in, the visual viewport has 20px of room to scroll. The user agent distributes the scroll by scrolling the visual viewport down by
20px and the layout viewport by 30px.
</div>

<div class='example'>
The user is viewing a document in a mobile user agent. The document focuses an offscreen text input element, showing a virtual keyboard which shrinks
the visual viewport. The user agent must now bring the element into view in the visual viewport. The user agent scrolls the layout viewport so that
the element is visible within it, then the visual viewport so that the element is visible to the user.
</div>

Scroll is <dfn lt="scroll completed">completed</dfn> when the scroll position has no more pending updates or translations and the user has completed their gesture. Scroll position updates include smooth or instant mouse wheel scrolling, keyboard scrolling, scroll-snap events, or other APIs and gestures which cause the scroll position to update and possibly interpolate. User gestures like touch panning or trackpad scrolling aren't complete until pointers or keys have released.

When a user agent is to perform a <dfn export id=concept-smooth-scroll>smooth scroll</dfn> of a <a>scrolling box</a> <var>box</var> to <var>position</var>,
Expand All @@ -386,7 +491,7 @@ To <dfn export>scroll to the beginning of the document</dfn> for a document <var
<a>scrolling area</a> with the <a>beginning edges</a> of <var>viewport</var>.
<li>If <var>position</var> is the same as <var>viewport</var>'s current scroll position, and <var>viewport</var> does not have an ongoing
<a>smooth scroll</a>, abort these steps.
<li><a>Perform a scroll</a> of <var>viewport</var> to <var>position</var>,
<li><a for="viewport">Perform a scroll</a> of <var>viewport</var> to <var>position</var>,
and <var>document</var>'s [=root element=] as the associated element, if there is one, or null otherwise.
</ol>

Expand Down Expand Up @@ -579,10 +684,13 @@ steps must be run:
1. If <var>position</var> is the same as the <a>viewport’s</a> current scroll position,
and the <a>viewport</a> does not have an ongoing <a>smooth scroll</a>, abort these steps.
1. Let <var>document</var> be the <a>viewport’s</a> associated {{Document}}.
1. <a>Perform a scroll</a> of the <a>viewport</a> to <var>position</var>,
1. <a for="viewport">Perform a scroll</a> of the <a>viewport</a> to <var>position</var>,
<var>document</var>'s [=root element=] as the associated element, if there is one, or null otherwise,
and the scroll behavior being the value of the {{ScrollOptions/behavior}} dictionary member of <var>options</var>.

Issue: User agents do not agree whether this uses the (coordinated) <a>viewport</a> <a for="viewport">perform a scroll</a> or the
<a>scrolling box</a> <a for="/">perform a scroll</a> on the layout viewport's scrolling box.

When the <dfn method for=Window lt="scrollTo(options)|scrollTo(x, y)">scrollTo()</dfn> method is invoked, the
user agent must act as if the {{Window/scroll()}} method was invoked with the same arguments.

Expand Down Expand Up @@ -1371,14 +1479,15 @@ a <a>scrolling box</a> <var>scrolling box</var>, in order of innermost to outerm
1. <dl class=switch>
<dt>If <var>scrolling box</var> is associated with an element
<dd>
Let <var>associated element</var> be the element.

<a for="/">Perform a scroll</a> of the element's <var>scrolling box</var> to <var>position</var>, with the element as the associated element and
<var>behavior</var> as the scroll behavior.
<dt>If <var>scrolling box</var> is associated with a <a>viewport</a>
<dd>
Let <var>document</var> be the <a>viewport’s</a> associated {{Document}}.
Let <var>associated element</var> be <var>document</var>'s [=root element=], if there is one, or null otherwise.
1. Let <var>document</var> be the <a>viewport’s</a> associated {{Document}}.
1. Let <var>root element</var> be <var>document</var>'s [=root element=], if there is one, or null otherwise.
1. <a for="viewport">Perform a scroll</a> of the <a>viewport</a> to <var>position</var>, with <var>root element</var> as the associated element and <var>behavior</var>
as the scroll behavior.
</dl>
1. <a>Perform a scroll</a> of <var>scrolling box</var> to <var>position</var>, <var>associated element</var> as the associated element and <var>behavior</var> as the scroll behavior.


To <dfn>scroll an element</dfn> <var>element</var> to <var>x</var>,<var>y</var> optionally with a scroll behavior <var>behavior</var> (which is "<code>auto</code>" if omitted) means to:
Expand All @@ -1400,7 +1509,7 @@ To <dfn>scroll an element</dfn> <var>element</var> to <var>x</var>,<var>y</var>
</dl>
1. Let <var>position</var> be the scroll position <var>box</var> would have by aligning <a>scrolling area</a> x-coordinate <var>x</var> with the left of <var>box</var> and aligning <a>scrolling area</a> y-coordinate <var>y</var> with the top of <var>box</var>.
1. If <var>position</var> is the same as <var>box</var>'s current scroll position, and <var>box</var> does not have an ongoing <a>smooth scroll</a>, abort these steps.
1. <a>Perform a scroll</a> of <var>box</var> to <var>position</var>, <var>element</var> as the associated element and <var>behavior</var> as the scroll behavior.
1. <a for="/">Perform a scroll</a> of <var>box</var> to <var>position</var>, <var>element</var> as the associated element and <var>behavior</var> as the scroll behavior.



Expand Down Expand Up @@ -1793,24 +1902,33 @@ Whenever an element gets scrolled (whether in response to user interaction or by
1. If the element is already in <var>doc</var>'s <a>pending scroll event targets</a>, abort these steps.
1. Append the element to <var>doc</var>'s <a>pending scroll event targets</a>.

Whenever a <a>visual viewport</a> gets scrolled (whether in response to user interaction or by an API), the user agent must run these steps:

1. Let <var>vv</var> be the {{VisualViewport}} object that was scrolled.
1. Let <var>doc</var> be <var>vv</var>'s <a for=visualviewport>associated document</a>.
1. If <var>vv</var> is already in <var>doc</var>'s <a>pending scroll event targets</a>, abort these steps.
1. Append <var>vv</var> to <var>doc</var>'s <a>pending scroll event targets</a>.

When asked to <dfn export for=Document>run the scroll steps</dfn> for a {{Document}} <var>doc</var>, run these steps:

1. For each item <var>target</var> in <var>doc</var>'s <a>pending scroll event targets</a>,
in the order they were added to the list, run these substeps:

1. If <var>target</var> is a {{Document}}, <a>fire an event</a> named <a event>scroll</a> that bubbles at <var>target</var> and <a>fire an event</a>
named <a event>scroll</a> at the {{VisualViewport}} that is associated with <var>target</var>.
1. If <var>target</var> is a {{Document}}, <a>fire an event</a> named <a event>scroll</a> that bubbles at <var>target</var>.
1. Otherwise, <a>fire an event</a> named <a event>scroll</a> at <var>target</var>.
1. Empty <var>doc</var>'s <a>pending scroll event targets</a>.

Whenever scrolling is <a lt="scroll completed">completed</a>, the user agent must run these steps:

1. If scrolling was done on a <a>viewport</a>, let <var>doc</var> be the <a>viewport’s</a> associated {{Document}} and <var>target</var> be the <a>viewport</a>.
Otherwise, scrolling is done on an element and let <var>doc</var> be the element's <a>node document</a> and <var>target</var> be the element.
1. If <var>target</var> is already in <var>doc</var>'s <a>pending scrollend event targets</a>, abort these steps.
1. Append <var>target</var> to <var>doc</var>'s <a>pending scrollend event targets</a>.
1. For each item <var>target</var> in <var>doc</var>'s <a>pending scrollend event targets</a>, in the order they were added to the list, run these substeps:
Issue: In what order are scrollend events dispatched? Ordered based on scroll start or scroll completion?

1. For each scrolling box <var>box</var> that was scrolled:
1. If <var>box</var> belongs to a <a>viewport</a>, let <var>doc</var> be the <a>viewport’s</a> associated {{Document}} and <var>target</var> be the <a>viewport</a>.
If <var>box</var> belongs to a {{VisualViewport}}, let <var>doc</var> be the {{VisualViewport}}'s <a for=visualviewport>associated document</a> and <var>target</var>
be the {{VisualViewport}}. Otherwise, <var>box</var> belongs to an element and let <var>doc</var> be the element's <a>node document</a> and <var>target</var> be the element.
1. If <var>target</var> is already in <var>doc</var>'s <a>pending scrollend event targets</a>, abort these steps.
1. Append <var>target</var> to <var>doc</var>'s <a>pending scrollend event targets</a>.
1. For each item <var>target</var> in <var>doc</var>'s <a>pending scrollend event targets</a>, in the order they were added to the list, run these substeps:
1. If <var>target</var> is a {{Document}}, <a>fire an event</a> named <a event>scrollend</a> that bubbles at <var>target</var>.
1. Otherwise, <a>fire an event</a> named <a event>scrollend</a> at <var>target</var>.
1. Empty <var>doc</var>'s <a>pending scrollend event targets</a>.
Expand All @@ -1836,14 +1954,15 @@ Whenever scrolling is <a lt="scroll completed">completed</a>, the user agent mus
<tr>
<td><dfn event for="Document, Element, VisualViewport">scroll</dfn>
<td>{{Event}}
<td>{{Document}}, elements, {{VisualViewport}}
<td>Fired at the {{Document}} or element when the <a>viewport</a> or element is scrolled, respectively. Fired at the {{VisualViewport}}
when the <a>visual viewport</a> is scrolled.
<td>{{VisualViewport}}, {{Document}}, elements
<td>Fired at the {{VisualViewport}}, {{Document}} or element when the {{VisualViewport}}, <a>viewport</a>, or element is scrolled, respectively.
<tr>
<td><dfn event for="Document, Element">scrollend</dfn>
<td><dfn event for="Document, Element, VisualViewport">scrollend</dfn>
<td>{{Event}}
<td>{{Document}}, elements
<td>Fired at the {{Document}} or element when scroll is <a lt="scroll completed">completed</a>: the <a>viewport</a> or element has been scrolled, the scroll sequence has ended and any scroll offset changes have been applied.
<td>{{Document}}, elements, {{VisualViewport}}
<td>Fired at the {{VisualViewport}}, {{Document}}, or element when a scroll is <a lt="scroll completed">completed</a>: the
{{VisualViewport}}, <a>viewport</a>, or element has been scrolled, the scroll sequence has ended and any scroll offset changes have
been applied.
</table>


Expand Down