Today I spent about an hour in writing a few very simple Intersection Observer tests, two hours in running them in a few browsers, and now an hour in writing down the results.
I’ve only just started my research, but can already draw a few odd conclusions, which make me fear Intersection Observers are not yet ready to be deployed on a large scale, particularly on mobile.
Intersection Observers are supposed to fire whenever an element (the target) scrolls into or out of a root viewport — and that can mean a wrapper element with
overflow: auto or the actual browser viewport. See this test page for the basic effect.
Those who’ve followed my blog for a long time probably know the first question I asked: “Browser viewport? Which browser viewport?” As usual, spec authors and article writers alike ignore this question entirely, while it is quite important for the mobile experience to know whether the observer uses the layout viewport or the visual viewport as its root.
And as you might have guessed, browsers use the wrong viewport here.
But I’m getting ahead of myself now. First have some useful articles:
The first test I created used a scrollable div as the root and a nested div as a target. When the target entered or exited the scrollable div’s viewport (i.e. when it became visible or invisible) the observer fired, just as one would expect.
I tested in Chrome/Mac, Chrome/Android, Samsung Internet, Firefox/Mac, Firefox/Android, and Edge. All of them handled this use case correctly. (Safari does not support Intersection Observers yet; neither on Mac nor on iOS.)
However, the first three, the Blink-based browsers, had one tiny, but telling bug. See the second test case on the page for the full details.
I currently suspect that the Blink-based browsers use the root’s padding box, and not its border box, as the actual viewport area.
That means that if the target element touches the root’s padding the browser fires an intersection observer, even though the target element is still fully visible within the box. To me, this is a bug. Not a huge one, but still a bug.
Even more interesting is the test that uses the browser viewport as root. As far as I’m concerned this is a very important use case: scrollable divs have their place in web development, but intersection observers are at their best when they tell you a certain element scrolls into the browser viewport and thus the user’s view.
Intersection observers expect an options object that may contain a
root. The default value is the browser viewport (which one? crickets). So I decided to test that.
In Firefox and Chrome on Mac it worked roughly as I expected. The intersection observer fired when the target element entered or left the browser window. This is what one would expect.
I have no idea what kind of default root element Edge 15 uses. It’s not the browser viewport, since the observer does not fire when the target element enters or exits the browser window. I thought it might be the HTML element (i.e. the full document), but that would mean the observer never fires. And it does fire once you make the browser window narrow enough vertically. Weird.
Then on to mobile. On desktop the layout viewport is equal to the visual viewport, but on mobile it’s not. Which viewport would intersection observers use?
Try for yourself once you’ve zoomed in a bit — and use my visualisation app to understand why this test proves the following.
Mobile browsers (Chrome, Samsung Internet, and Firefox) all use the layout viewport as their root element. And this is the wrong viewport.
What we want to know is when the user starts seeing the target element; in other words, when it moves into or out of the visual viewport. But when you’ve zoomed in the intersection observer and the user-viewed area go out of sync, since the browsers wrongly use the layout viewport as their root.
So there you go. Unusable on mobile, badly damaged in Edge, and a small but potentially annoying bug in Blink. Intersection observers have not yet come to stay.
If you like this blog, why not donate a little bit of money to help me pay my bills?