Closed Bug 1860030 Opened 2 years ago Closed 6 months ago

Implement IntersectionObserver scrollMargin

Categories

(Core :: Layout, enhancement)

enhancement

Tracking

()

RESOLVED FIXED
141 Branch
Size Estimate S
Tracking Status
firefox120 --- wontfix
firefox141 --- fixed

People

(Reporter: zcorpan, Assigned: keithamus)

References

(Depends on 1 open bug, Blocks 2 open bugs)

Details

(Keywords: dev-doc-complete, parity-chrome, webcompat:platform-bug, Whiteboard: [platform-feature][webcompat:risk-low])

Attachments

(1 file)

Spec PR: https://github.com/w3c/IntersectionObserver/pull/511
Spec issue: https://github.com/w3c/IntersectionObserver/issues/431

This feature would be good for improving the user experience for lazy-loading images and iframes in scrollable containers.

Keywords: dev-doc-needed
Duplicate of this bug: 1862880
Blocks: 1864794
Assignee: nobody → mozilla
Status: NEW → ASSIGNED
Depends on: 1867157
Status: ASSIGNED → RESOLVED
Closed: 6 months ago
Resolution: --- → FIXED
Target Milestone: --- → 141 Branch
Size Estimate: --- → S
Flags: in-testsuite+

Did a follow-up get filed for the root scroller behavior? It seems the margin should be doubled in that case.

Flags: needinfo?(mozilla)

(As in, both rootMargin and scrollMargin should apply, not literal doubling)

Yeah working on a test & followup!

Flags: needinfo?(mozilla)
Depends on: 1971717

FF141 MDN work for this can be tracked in https://github.com/mdn/content/issues/40026

Can you please help me understand how this affects the intersection calculation? The spec says:

Offsets are applied to scrollports on the path from intersection root to target, effectively growing or shrinking the clip rects used to calculate intersections.

I can see from the guide that the rootMargin allows you to grow or shrink the root area used for calculating intersections. I assume the use case for that is that perhaps you want to start fetching things before you fully intersect? Anyway, it is clear how this works from the example.

But what is scrollmargin for? My understanding is that this is used to set where a scroll snap will end up. So you're scrolling part of an element into view, and if you don't pull enough and let go, the part you pulled will revert.
So my assumption here is that you're saying "maybe I don't want to "count" the intersection until the scroll margin as been reached and now I know that if I let go the target element will move fully into the view.
All guesses. Can you confirm/explain?

PS It might help to imagine you were writing a release note and you wanted to make it clear why this is useful. I'll have to do that :-)

Flags: needinfo?(mozilla)

I assume the use case for that is that perhaps you want to start fetching things before you fully intersect

This is correct, it's also correct for scrollMargin. They're both used to grow the area you want to report on.

My understanding is that this is used to set where a scroll snap will end up. So you're scrolling part of an element into view, and if you don't pull enough and let go, the part you pulled will revert.

This doesn't gel with my understanding. I don't think it's related to scroll snap in a way that makes this useful for that.

But what is scrollmargin for?

scrollMargin can be thought of like a rootMargin that is applied for all containers that are scroll containers. In other words during the calculation of intersections, the target will traverse up the tree for each container and apply scrollMargin to any container that has overflow: scroll/overflow: hidden etc. Finally when it reaches the root container the rootmargin is applied. The effect is applied cumulatively, so for example a rootMargin of 10px with a scrollMargin of 20px means that if the root element is a scroller the effective rect is inflated by 30px (10px + 20px).

This means if you want to achieve the effect of "fetching content before it's fully in view" then scrollMargin becomes more useful because you can observe intersections on nested scrollers while observing with a root element which does not have to be the scroller you're concerned about, for example the root can be document.body (which is likely to be a scroll container) but scrollMargin would apply on a <div id=sidebar> or <div class=carousel> or whatever nested element is inside.

I can rustle up an example if it would help.

Flags: needinfo?(mozilla)

Thanks much @keith.
An example would be wonderful thanks. I hope to integrate something to the example here: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#the_intersection_root_and_root_margin

I "almost" think I get it, which means I probably don't.
Based on what you said, the explainer and the chrome status

Intersection Observer scrollMargin allows developers to observe targets inside nested scroll containers that are currently clipped away by the scroll containers. This is achieved by expanding the container's clipping rect by the scrollMargin when calculating the intersection.

So the way I imagine this is, that I have a target element inside a scrollable container (and might be scrolled out of sight in that container), and this is in turn inside the body (which might also be a scroll container).

As the target's scrollable container is scrolled into the viewport it isn't clear to me what happens either with or without the scrollMargin.

It could be that the observer

  1. Only sees the target when it is visible in its scrollable container (so if you scroll the container into view but the target is hidden there are no intersections), OR
  2. Treats the element as though it was not in a scrollable container. For the purpose of intersection calculations it is as though the target was in a scrollable container as big as the container if it was not clipped.
  3. Something else.

The discussion around scrollMargin sounds like we're using the margin to artificially grow the target's clip region. What I think then is that what we have is option 1 - the element intersection is only observed when the element is actually visible in its containing scroll container.
What the margin does is make that scroll container look bigger for the purpose of calculating intersections, so we can see it earlier. But we still only observe it when it become effectively visible in the clipping region. Could that be about right?

I think that might be what you mean by "because you can observe intersections on nested scrollers while observing with a root element which does not have to be the scroller you're concerned about,"

Hopefully an example would make this real for me. I do feel dumb.

Flags: needinfo?(mozilla)

It's (3), but this is tricky. :-)

Here is an example, using lazy-loading images in a carousel, to show why this is useful and why expanding the scroller's clipping by the scrollMargin value for the purpose of intersection calculation helps.

https://software.hixie.ch/utilities/js/live-dom-viewer/saved/13880

First, scroll down to the carousel in the iframe so that it is fully visible. As you scroll the carousel sideways, you would want the images to be loaded already when they become visible in the scrollbox, instead of only starting to load after they become visible.

If the browser were to apply for example 200px scrollMargin to the internal IntersectionObserver (visually represented by the outline), then the image would be "intersecting" 200px before it becomes actually visible, and thus trigger the load in time for a better UX.

Flags: needinfo?(mozilla)

@zcorpan @keith. Thank you. That is tricky. I modified the example so that it works in this gist https://gist.github.com/hamishwillee/7a10a556ed5edffddc85a501b93f00c6

I now have about 90% confidence I understand.

So we have an IntersectionObserver with the root on the viewport that contains a nested scroller with a bunch of cat images.
If we have no scrollMargin then each cat becomes visible at the earliest when it is scrolled into view.
You can't set the rootMargin to make the whole carousel visible earlier, but that won't make the contents of the scroller visible until they are in view.

So by setting a scroll margin on any scroller, we're effectively making the scroller bigger with respect to determining if something is about to become visible within that scroller.

It still only "applies" when the scroll container (carousel) is visible too.

Right?

If so, thanks, I think I can write this now. Stealing your example <evil snigger>

Any interest in reviewing this when it is done?

Flags: needinfo?(zcorpan)
Flags: needinfo?(mozilla)

Sounds like you've got a good understanding of it! More than happy to review any docs.

Flags: needinfo?(mozilla)
Flags: needinfo?(zcorpan)
Whiteboard: [platform-feature][webcompat:risk-low]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: