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

Add visual viewport scrollend event ref page #34427

Merged
merged 13 commits into from
Aug 27, 2024

Conversation

chrisdavidmills
Copy link
Contributor

Description

Chrome 126 adds the onscrollend handler property. See the associated ChromeStatus entry. This PR adds a reference page for scrollend/onscrollend.

Motivation

Additional details

Related issues and pull requests

@chrisdavidmills chrisdavidmills requested a review from a team as a code owner June 26, 2024 13:51
@chrisdavidmills chrisdavidmills requested review from wbamberg and removed request for a team June 26, 2024 13:51
@github-actions github-actions bot added Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed labels Jun 26, 2024
Copy link
Contributor

github-actions bot commented Jun 26, 2024

@@ -18,7 +18,7 @@ The mobile web contains two viewports, the layout viewport and the visual viewpo

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.

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.
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 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.
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the example below it says "You invoke this function by passing it to both event calls." - from what I can understand it we don't need to run this on scrollend (?) but should correct the language "both" anyway. And this sort of begs the question, when is scrollend useful?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have updated that sentence to "viewportHandler() is passed in as a handler for the resize and scroll events." in my next commit.

I'll level with you though — I played with it quite a lot, but could not find a use case for scrollend.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, I've got some better information now. I've updated the text to talk more about real use cases and replaced the example on this page with a better, more understandable example.

The live demo link currently links to a Glitch instance, but I am more than happy to put it on dom-examples instead, once we get agreement that the demo is in a good state.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you point me to the actual code? I never know how to find it from a Glitch URL.


{{APIRef("Visual Viewport")}}

The **`scrollend`** event of the {{domxref("VisualViewport")}} interface is fired when a scrolling operation on the visual viewport ends.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be great to have a sentence or two about why this event is useful: why I might want to listen to it and what I might want to do when it fires.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added!

Comment on lines 29 to 43
You can use the `scrollend` event in an {{domxref("EventTarget.addEventListener", "addEventListener()")}} method:

```js
visualViewport.addEventListener("scrollend", () => {
// …
});
```

Or use the `onscrollend` event handler property:

```js
visualViewport.onscrollend = () => {
// …
};
```
Copy link
Collaborator

Choose a reason for hiding this comment

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

These aren't useful examples, they really just show generic event syntax. Is there a somewhat realistic example we could have, that shows a use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See above. I'll ask the engineers at Google if they have an idea for a use case I could show.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Example added on the main visual viewport API page; I've linked to it from here rather than repeating it.

Copy link
Collaborator

@wbamberg wbamberg left a comment

Choose a reason for hiding this comment

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

Yes, I like this example. I'd like it to be in dom-examples obviously, and had a few picky comments, but overall it makes sense to me.

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.
Our [Visual viewport events](https://visual-viewport-events.glitch.me/) example provides a basic demonstration of how the different visual viewport features work, including the three event types. Load the page in a supporting mobile browser and try pinch-zooming and panning around the boxes. On `resize` and `scroll`, the "Total" box is repositioned to keep the same position relative to the visual viewport. On `scrollend`, it updates to show which boxes are in view, and the sum of their numbers. A more complex application could use these techniques to scroll around map tiles and display relevant information for each one shown on the screen.

The HTML and CSS for this example is fairly basic, and we won't explain it here for brevity. You can check it out at the example link above.
Copy link
Collaborator

@wbamberg wbamberg Aug 1, 2024

Choose a reason for hiding this comment

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

From the link given (https://visual-viewport-events.glitch.me/) I don't know how to see the HTML and CSS. If we don't explain it, we should link to it.

(actually, at least the HTML we should IMO quote because the JS does a lot of accessing the DOM elements)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll create a PR to add this demo to dom-examples after this, and update the links when it is merged.

In my next commit to this PR, I have added the HTML code listing, and a brief explanation of it.


```js
const total = document.getElementById("total");
let visibleBoxes = [];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is "logically const" it might be better to declare it const and empty it by setting length to zero.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, I agree with this; update made in my next commit.

1 / viewport.scale
})`;
});
function IsVisible(box) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
function IsVisible(box) {
function isVisible(box) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Updated.

Comment on lines 73 to 76
const x = box.offsetLeft,
y = box.offsetTop;
const right = x + box.offsetWidth,
bottom = y + box.offsetHeight;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call; updated.

const horLowerBound = window.scrollX + visualViewport.offsetLeft;
const horUpperBound =
window.scrollX + visualViewport.offsetLeft + visualViewport.width;
let hor =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could be const? Also horizontal is a better name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

const verLowerBound = window.scrollY + visualViewport.offsetTop;
const verUpperBound =
window.scrollY + visualViewport.offsetTop + visualViewport.height;
let ver =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could be const? Also vertical is a better name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

visibleBoxes = [];

for (const box of boxes) {
if (IsVisible(box)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (IsVisible(box)) {
if (isVisible(box)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

```js
function updateSum() {
let sumTotal = 0;
let summands = [];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let summands = [];
const summands = [];

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

@@ -18,7 +18,7 @@ The mobile web contains two viewports, the layout viewport and the visual viewpo

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.

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.
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 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.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you point me to the actual code? I never know how to find it from a Glitch URL.

visualViewport.onscrollend = scrollendUpdater;
window.onresize = scrollUpdater;
window.onscroll = scrollUpdater;
window.onscrollend = scrollendUpdater;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why we have to listen to the Window events as well as the ``visualViewport` ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added some explanation to the page to cover this:

Now we set event handler properties on both the visual viewport and the Window object to run the key functions at the appropriate times on both mobile and desktop:

  • We set the handlers on window so that the total box position and contents will update on conventional window scrolling operations, for example when you scroll the page on a desktop browser.
  • We set the handlers on visualViewport so that the total box position and contents will update on visual viewport scrolling/zooming operations, for example when you pinch-zoom and then scroll the page on a mobile browser.

@chrisdavidmills
Copy link
Contributor Author

@wbamberg FYI, I've added the demo to the dom-examples repo in mdn/dom-examples#279.

@chrisdavidmills
Copy link
Contributor Author

OK @wbamberg, the code explanation has been updated to match the dom-examples demo.

Copy link
Collaborator

@wbamberg wbamberg left a comment

Choose a reason for hiding this comment

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

Thank you for the updates!

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

## Examples

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.
Our [Visual viewport events](https://visual-viewport-events.glitch.me/) 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 displayed grid of boxes. Also try pinch-zooming on the mobile browser. 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`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This needs updating still (refers to glitch and grid of boxes).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, I've updated both now.

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.
Our [Visual viewport events](https://visual-viewport-events.glitch.me/) 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 displayed grid of boxes. Also try pinch-zooming on the mobile browser. 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`.

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.
Copy link
Collaborator

Choose a reason for hiding this comment

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

The text here and elsewhere seems to imply that pinch-zooming is only possible on mobile, but FWIW I find that pinch-zooming also works on the laptop trackpad.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've updated these descriptions too.

files/en-us/web/api/visual_viewport_api/index.md Outdated Show resolved Hide resolved
@chrisdavidmills
Copy link
Contributor Author

chrisdavidmills commented Aug 27, 2024

@wbamberg I also updated the dom-examples demo so that the HTML content is the same in both places (I tweaked the example HTML based on your comment about pinch-zoom): mdn/dom-examples#280

Can you merge that one too?

Copy link
Collaborator

@wbamberg wbamberg left a comment

Choose a reason for hiding this comment

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

👍 thank you for your patience with me Chris.

@wbamberg wbamberg merged commit 4b5b3e1 into mdn:main Aug 27, 2024
8 checks passed
@chrisdavidmills chrisdavidmills deleted the visualviewport-onscrollend branch August 28, 2024 08:32
@chrisdavidmills
Copy link
Contributor Author

👍 thank you for your patience with me Chris.

@wbamberg from one grumpy Englishman to another, thank you so much for your excellent review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants