Skip to content

Commit 4b5b3e1

Browse files
Add visual viewport scrollend event ref page (mdn#34427)
* Add visual viewport scrollend event ref page * correct scrollend linking errors * Fix for wbamberg review comment * Added useful example and better use case explanations * fixes for wbamberg 2nd round review comments * Update code explanation as the demo has changed * Update files/en-us/web/api/visual_viewport_api/index.md Co-authored-by: wbamberg <[email protected]> * Last few fixes for wbamberg comments --------- Co-authored-by: wbamberg <[email protected]>
1 parent 9380313 commit 4b5b3e1

File tree

5 files changed

+120
-64
lines changed

5 files changed

+120
-64
lines changed

files/en-us/web/api/visual_viewport_api/index.md

Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ The **Visual Viewport API** provides an explicit mechanism for querying and modi
1616

1717
The mobile web contains two viewports, the layout viewport and the visual viewport. The layout viewport covers all the elements on a page and the visual viewport is what is actually visible on the screen. When the user pinch-zooms into the page, the visual viewport shrinks but the layout viewport is unchanged. User-interface features like the on-screen keyboard (OSK) can shrink the visual viewport without affecting the layout viewport.
1818

19-
What happens when a web page element needs to be visible on screen regardless of the visible portion of a web page? For example, what if you need a set of image controls to remain on screen regardless of the pinch zoom level of the device? Current browsers vary in how they handle this. The visual viewport lets web developers solve this by positioning elements relative to what's shown on screen.
19+
What happens when a web page element needs to be visible on screen regardless of the visible portion of a web page? For example, what if you need a set of image controls to remain on screen regardless of the pinch-zoom level of the device? Current browsers vary in how they handle this. The visual viewport lets web developers solve this by positioning elements relative to what's shown on-screen.
2020

21-
To access a window's visual viewport, you can obtain a {{domxref("VisualViewport")}} object from the {{domxref("window.visualViewport")}} property. The object includes a set of properties describing the viewport. It also adds two events, `onresize` and `onscroll`, that fire whenever the visual viewport changes. These events allow you to position elements relative to the visual viewport that would normally be anchored to the layout viewport.
21+
To access a window's visual viewport, you can obtain a {{domxref("VisualViewport")}} object from the {{domxref("window.visualViewport")}} property. The object includes a set of properties describing the viewport. It also adds three events, {{domxref("VisualViewport/resize_event", "resize")}}, {{domxref("VisualViewport/scroll_event", "scroll")}}, and {{domxref("VisualViewport/scrollend_event", "scrollend")}}, which fire when the visual viewport is resized, scrolls, and finishes a scrolling action, respectively.
22+
23+
The first two events allow you to position elements relative to the visual viewport as it is scrolled or zoomed, which would normally be anchored to the layout viewport. The `scrollend` event allows you to update an element when a scrolling action is completed. For example, you can use these events to keep an element fixed to the visual viewport as it is pinch-zoomed and scrolled, and update it when scrolling ends.
2224

2325
## Interfaces
2426

2527
- {{DOMxRef("VisualViewport")}}
26-
- : Represents the visual viewport for a given window. A window's `VisualViewport` object provides information about the viewport's position and size, and receives the {{domxref("VisualViewport.resize_event", "resize")}} and {{domxref("VisualViewport.scroll_event", "scroll")}} events you can monitor to know when changes occur to the window's viewport.
28+
- : Represents the visual viewport for a given window. A window's `VisualViewport` object provides information about the viewport's position and size, and receives the {{domxref("VisualViewport.resize_event", "resize")}}, {{domxref("VisualViewport.scroll_event", "scroll")}} and {{domxref("VisualViewport.scrollend_event", "scrollend")}} events.
2729

2830
### Extensions to other interfaces
2931

@@ -32,42 +34,84 @@ To access a window's visual viewport, you can obtain a {{domxref("VisualViewport
3234

3335
## Examples
3436

35-
The code below is based on [the sample in the specification](https://github.com/WICG/visual-viewport/blob/gh-pages/examples/fixed-to-viewport.html), though it adds a few things that make it function better. It shows a function called `viewportHandler()`. When called it queries the `offsetLeft` and `height` properties for values it uses in a CSS `translate()` method. You invoke this function by passing it to _both_ event calls.
37+
Our [Visual Viewport API](https://mdn.github.io/dom-examples/visual-viewport-api/) example provides a basic demonstration of how the different visual viewport features work, including the three event types. Load the page in supporting desktop and mobile browsers and try scrolling around the page and pinch-zooming. On `resize` and `scroll`, the information box is repositioned to keep the same position relative to the visual viewport, and the viewport and scroll information it shows is updated. Also, on `resize` and `scroll` we change the box color to indicate something is happening, changing it back on `scrollend`.
38+
39+
You'll find that on desktop browsers the {{domxref("Window.scrollX")}} and {{domxref("Window.scrollY")}} values are updated as the window is scrolled — the visual viewport position does not change. On mobile browsers however, the {{domxref("VisualViewport.offsetLeft")}} and {{domxref("VisualViewport.offsetTop")}} values are generally updated — it is usually the visual viewport that changes rather than the window position.
40+
41+
The example HTML can be seen below. The information box is represented by a {{htmlelement("div")}} with an `id` of `output`.
42+
43+
```html
44+
<p id="instructions">
45+
Try scrolling around and pinch-zooming to see how the reported values change.
46+
</p>
47+
<div id="output">
48+
<p id="visual-info"></p>
49+
<hr />
50+
<p id="window-info"></p>
51+
</div>
52+
```
53+
54+
We won't explain the example's CSS for the sake of brevity — it is not important for understanding the demo. You can check it out at the example link above.
55+
56+
In the JavaScript, we start by getting references to the information box we'll be updating as the page is zoomed and scrolled, as well as the two paragraphs contained within it. The first one will contain reported {{domxref("VisualViewport.offsetLeft")}} and {{domxref("VisualViewport.offsetTop")}} values, while the second one will contain reported {{domxref("Window.scrollX")}} and {{domxref("Window.scrollY")}} values.
57+
58+
```js
59+
const output = document.getElementById("output");
60+
const visualInfo = document.getElementById("visual-info");
61+
const windowInfo = document.getElementById("window-info");
62+
```
63+
64+
Next, we define the two key functions we'll run when the events fire:
3665

37-
One thing that may not be clear in this example is the use of the `pendingUpdate` flag and the call to `requestAnimationFrame()`. The `pendingUpdate` flag serves to prevent multiple invocations of the transform that can occur when `onresize` and `onscroll` fire at the same time. Using `requestAnimationFrame()` ensures that the transform occurs before the next render.
66+
- `scrollUpdater()` will fire on `resize` and `scroll`: this function updates the position of the information box relative to the visual viewport by querying the {{domxref("VisualViewport.offsetTop")}} and {{domxref("VisualViewport.offsetLeft")}} properties and using their values to update the values of the relevant {{glossary("inset properties")}}. We also change the information box's background color to indicate that something is happening, and run the `updateText()` function to update the values shown in the box.
67+
- The `scrollEndUpdater()` function will fire on `scrollend`: this returns the information box to its original color and runs the `updateText()` function to make sure the latest values are shown on `scrollend`.
3868

3969
```js
40-
let pendingUpdate = false;
41-
42-
function viewportHandler(event) {
43-
if (pendingUpdate) return;
44-
pendingUpdate = true;
45-
46-
requestAnimationFrame(() => {
47-
pendingUpdate = false;
48-
const layoutViewport = document.getElementById("layoutViewport");
49-
50-
// Since the bar is position: fixed we need to offset it by the
51-
// visual viewport's offset from the layout viewport origin.
52-
const viewport = event.target;
53-
const offsetLeft = viewport.offsetLeft;
54-
const offsetTop =
55-
viewport.height -
56-
layoutViewport.getBoundingClientRect().height +
57-
viewport.offsetTop;
58-
59-
// You could also do this by setting style.left and style.top if you
60-
// use width: 100% instead.
61-
bottomBar.style.transform = `translate(${offsetLeft}px, ${offsetTop}px) scale(${
62-
1 / viewport.scale
63-
})`;
64-
});
70+
const scrollUpdater = () => {
71+
output.style.top = `${visualViewport.offsetTop + 10}px`;
72+
output.style.left = `${visualViewport.offsetLeft + 10}px`;
73+
output.style.background = "yellow";
74+
updateText();
75+
};
76+
77+
const scrollendUpdater = () => {
78+
output.style.background = "lime";
79+
updateText();
80+
};
81+
```
82+
83+
The `updateText()` function looks like so — it sets the {{domxref("HTMLElement.innerText")}} of the first paragraph to show the current `VisualViewport.offsetLeft` and `VisualViewport.offsetTop` values, and the `HTMLElement.innerText` of the second paragraph to show the current `Window.scrollX` and `Window.scrollY` values. After defining `updateText()`, we immediately invoke it so that the information box displays correctly on page load.
84+
85+
```js
86+
function updateText() {
87+
visualInfo.innerText = `Visual viewport left: ${visualViewport.offsetLeft.toFixed(2)}
88+
top: ${visualViewport.offsetTop.toFixed(2)}`;
89+
windowInfo.innerText = `Window scrollX: ${window.scrollX.toFixed(2)}
90+
scrollY: ${window.scrollY.toFixed(2)}`;
6591
}
6692

67-
window.visualViewport.addEventListener("scroll", viewportHandler);
68-
window.visualViewport.addEventListener("resize", viewportHandler);
93+
updateText();
6994
```
7095

96+
> [!NOTE]
97+
> We truncate all values to two decimal places using the {{jsxref("Number.toFixed()")}} method because some browsers display them as a subpixel value, potentially with a large number of decimal places.
98+
99+
Now we set event handler properties on both the visual viewport and the {{domxref("Window")}} object to run the key functions at the appropriate times on both mobile and desktop:
100+
101+
- We set the handlers on `window` so that the information box position and contents will update on conventional window scrolling operations, for example when you scroll the page on a desktop browser.
102+
- We set the handlers on `visualViewport` so that the information box position and contents will update on visual viewport scrolling/zooming operations, for example when you scroll and pinch-zoom the page on a mobile browser.
103+
104+
```js
105+
visualViewport.onresize = scrollUpdater;
106+
visualViewport.onscroll = scrollUpdater;
107+
visualViewport.onscrollend = scrollendUpdater;
108+
window.onresize = scrollUpdater;
109+
window.onscroll = scrollUpdater;
110+
window.onscrollend = scrollendUpdater;
111+
```
112+
113+
`scrollUpdater()` will fire on `resize` and `scroll`, while `scrollEndUpdater()` will fire on `scrollend`.
114+
71115
## Specifications
72116

73117
{{Specifications}}

files/en-us/web/api/visualviewport/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ Listen to these events using {{domxref("EventTarget.addEventListener", "addEvent
4949
- {{domxref("VisualViewport/scroll_event", "scroll")}}
5050
- : Fired when the visual viewport is scrolled.
5151
Also available via the `onscroll` property.
52+
- {{domxref("VisualViewport/scrollend_event", "scrollend")}}
53+
- : Fired when a scrolling operation on the visual viewport ends.
54+
Also available via the `onscrollend` property.
5255

5356
## Examples
5457

files/en-us/web/api/visualviewport/resize_event/index.md

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ browser-compat: api.VisualViewport.resize_event
88

99
{{APIRef("Visual Viewport")}}
1010

11-
The **`resize`** event of the {{domxref("VisualViewport")}} interface is fired when the visual viewport is resized.
11+
The **`resize`** event of the {{domxref("VisualViewport")}} interface is fired when the visual viewport is resized. This allows you to position elements relative to the visual viewport as it is zoomed, which would normally be anchored to the layout viewport.
1212

1313
## Syntax
1414

@@ -26,21 +26,7 @@ A generic {{domxref("Event")}}.
2626

2727
## Examples
2828

29-
You can use the `resize` event in an {{domxref("EventTarget.addEventListener", "addEventListener()")}} method:
30-
31-
```js
32-
visualViewport.addEventListener("resize", () => {
33-
//
34-
});
35-
```
36-
37-
Or use the `onresize` event handler property:
38-
39-
```js
40-
visualViewport.onresize = () => {
41-
//
42-
};
43-
```
29+
See the [Visual Viewport API](/en-US/docs/Web/API/Visual_Viewport_API#examples) landing page for a usage demo.
4430

4531
## Specifications
4632

files/en-us/web/api/visualviewport/scroll_event/index.md

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ browser-compat: api.VisualViewport.scroll_event
88

99
{{APIRef("Visual Viewport")}}
1010

11-
The **`scroll`** event of the {{domxref("VisualViewport")}} interface is fired when the visual viewport is scrolled.
11+
The **`scroll`** event of the {{domxref("VisualViewport")}} interface is fired when the visual viewport is scrolled. This allows you to position elements relative to the visual viewport as it is scrolled, which would normally be anchored to the layout viewport.
1212

1313
## Syntax
1414

@@ -26,21 +26,7 @@ A generic {{domxref("Event")}}.
2626

2727
## Examples
2828

29-
You can use the `scroll` event in an {{domxref("EventTarget.addEventListener", "addEventListener()")}} method:
30-
31-
```js
32-
visualViewport.addEventListener("scroll", () => {
33-
//
34-
});
35-
```
36-
37-
Or use the `onscroll` event handler property:
38-
39-
```js
40-
visualViewport.onscroll = () => {
41-
//
42-
};
43-
```
29+
See the [Visual Viewport API](/en-US/docs/Web/API/Visual_Viewport_API#examples) landing page for a usage demo.
4430

4531
## Specifications
4632

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
title: "VisualViewport: scrollend event"
3+
short-title: scrollend
4+
slug: Web/API/VisualViewport/scrollend_event
5+
page-type: web-api-event
6+
browser-compat: api.VisualViewport.scrollend_event
7+
---
8+
9+
{{APIRef("Visual Viewport")}}
10+
11+
The **`scrollend`** event of the {{domxref("VisualViewport")}} interface is fired when a scrolling operation on the visual viewport ends. This allows you to update an element when a scrolling action is completed. For example, you could use the {{domxref("VisualViewport/resize_event", "resize")}} and {{domxref("VisualViewport/scroll_event", "scroll")}} events to keep an element fixed to the visual viewport as it is pinch-zoomed and scrolled, and update it with new content when scrolling ends using `scrollend`.
12+
13+
## Syntax
14+
15+
Use the event name in methods like {{domxref("EventTarget.addEventListener", "addEventListener()")}}, or set an event handler property.
16+
17+
```js
18+
addEventListener("scrollend", (event) => {});
19+
20+
onscrollend = (event) => {};
21+
```
22+
23+
## Event type
24+
25+
A generic {{domxref("Event")}}.
26+
27+
## Examples
28+
29+
See the [Visual Viewport API](/en-US/docs/Web/API/Visual_Viewport_API#examples) landing page for a usage demo.
30+
31+
## Specifications
32+
33+
{{Specifications}}
34+
35+
## Browser compatibility
36+
37+
{{Compat}}

0 commit comments

Comments
 (0)