-
Notifications
You must be signed in to change notification settings - Fork 22.5k
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
Add visual viewport scrollend event ref page #34427
Conversation
Preview URLs
External URLs (1)URL:
(comment last updated: 2024-08-27 10:23:44) |
@@ -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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added!
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 = () => { | ||
// … | ||
}; | ||
``` |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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. |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 = []; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function IsVisible(box) { | |
function isVisible(box) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Updated.
const x = box.offsetLeft, | ||
y = box.offsetTop; | ||
const right = x + box.offsetWidth, | ||
bottom = y + box.offsetHeight; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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 = |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 = |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (IsVisible(box)) { | |
if (isVisible(box)) { |
There was a problem hiding this comment.
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 = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let summands = []; | |
const summands = []; |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@wbamberg FYI, I've added the demo to the |
OK @wbamberg, the code explanation has been updated to match the |
There was a problem hiding this 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`. |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@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? |
There was a problem hiding this 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 from one grumpy Englishman to another, thank you so much for your excellent review! |
Description
Chrome 126 adds the
onscrollend
handler property. See the associated ChromeStatus entry. This PR adds a reference page forscrollend
/onscrollend
.Motivation
Additional details
Related issues and pull requests