Bug 20246 - Provide a system to observe nodes entering and leaving the viewport
Provide a system to observe nodes entering and leaving the viewport
Status: NEW
Product: CSS
Classification: Unclassified
Component: CSSOM View
unspecified
All All
: P2 normal
: ---
Assigned To: Simon Pieters
public-css-bugzilla
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2012-12-05 15:11 UTC by louisremi
Modified: 2013-08-08 13:28 UTC (History)
10 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description louisremi 2012-12-05 15:11:58 UTC
Loading content as the user scrolls the page is one of the best ways to improve performances on modern website. Unfortunately, there aren't any simple and efficient APIs to do that. I propose a new API to cover those needs.

Currently, the most common way to implement "lazy loading" in a page is to bind a listener to the scroll event that checks the position of a limited set of nodes, relative to the view-port. But there are many other conditions that can cause nodes to enter or leave viewport with triggering events: changing the dimensions of watched elements, switching their display style property or adding/removing elements to the same container.

Similar to MutationObserver, we could have a ViewportObserver with very similar API:

// select the target node
var target = document.querySelector('#some-id');
  
// create an observer instance
var observer = new ViewportObserver(function( changes ) {
  // This callback receives a ViewportRecord with following properties
  console.log( changes.enteringNodes, changes.leavingNodes );
});
  
// configuration of the observer:
var config = { subtree: true };

// pass in the target node, as well as the observer options (ViewportObserverInit)
observer.observe( target, config );

// later, you can stop observing
observer.disconnect();


This API would allow to write much more flexible, efficient and reliable lazy-loading logic, with much less lines of code than what is currently required.

Implementation-wise, only the position of observed elements should be checked. The visibility (which can be affected by many style properties including "visibility", "z-index", "opacity", "clip", "overflow") isn't taken into account; it would be the user's job to handle that if necessary.
Comment 1 Marcos Caceres 2012-12-05 15:24:08 UTC
@louisremi is there another use case apart from lazy loading images? What else is usually lazy loaded. Also, is could you point to some libs that devs are currently using to achieve this?
Comment 2 louisremi 2012-12-05 16:32:47 UTC
@marcos One could want to load additional content once the user scrolls up to a certain position in the page. This is what is done on makr.io[1] for example.

There are two libraries I know of that handle image lazy-loading: LazyLoad[2] and lazyload[3]. I've also created a small script able to watch elements stacked vertically in a common container[4].
All of these implementations have the limitations I mentioned in my initial comment, but lazylod is capable of watching new images as they're added to the document if they have an onload="lzld(this)" attribute.

[1] https://makr.io/
[2] https://github.com/darkwing/LazyLoad/blob/master/Source/LazyLoad.js
[3] https://github.com/vvo/lazyload/blob/master/lazyload.js
[4] http://jsbin.com/etuvav/5/edit
Comment 3 Marcos Caceres 2012-12-05 16:37:54 UTC
@lousremi thanks for the links, they are helpful (if you find any more, please add them as they serve as a evidence for the need of this). 

Regarding images, this might be relevant to the discussion: https://www.w3.org/Bugs/Public/show_bug.cgi?id=17842
Comment 4 louisremi 2012-12-05 17:43:43 UTC
Another use-case I forgot to mention is the ability to remove things from the DOM as they leave the viewport (just to free up memory) or to pause CSS3 Animations and requestAnimationFrame loops.
Comment 5 Jake Archibald 2012-12-06 14:26:46 UTC
@louisremi the requestanimationframe optimisation stuff should be handled by the second param of raf itself
Comment 6 Olli Pettay 2012-12-06 14:30:02 UTC
(In reply to comment #5)
> @louisremi the requestanimationframe optimisation stuff should be handled by
> the second param of raf itself

requestAnimationFrame takes only one parameter, atm.
http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html#dom-windowanimationtiming-requestanimationframe
Comment 7 Jake Archibald 2012-12-06 18:03:48 UTC
(In reply to comment #6)
> requestAnimationFrame takes only one parameter, atm.
> http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/
> Overview.html#dom-windowanimationtiming-requestanimationframe

A damn fine point sir, I thought it was in the spec. In that case: the raf use case *should* be handled by an element parameter in raf.
Comment 8 louisremi 2012-12-06 18:21:34 UTC
@jakearchibald from what I understand about browser rendering engine, it is very tricky to determine when an element is visible or not, as "what is rendered on the screen" is the result of many operations on different levels, some of them being trusted to the hardware. (anyone more knowledgeable than me on the matter: correct me if I'm wrong).

It is usually easier for the developer to know what CSS might affect the visibility of an element, and ViewportObserver would provide the missing piece of information to decide when animations should be turned on and off.
Comment 9 Yoav Weiss 2013-06-24 08:37:16 UTC
@louisremi - If browsers can expose which nodes enter or leave the viewport, they can also use that info to apply their own logic (e.g. stop animating elements that are not in the viewport).

I think the major use-case here is for lazy loading of content (prob. mostly images & HTML/JSON).
The images case is (partially) handled by the proposed "lazyload" attribute[1]. It doesn't give the author control over when & how (nor if) the image will in fact lazy load. That control stays in the browser (which doesn't match the extensible-Web philosophy). It does guaranty that these images won't block onload though.
 
Personally, I think the proposal is interesting and can be useful as a low-level API that enables libraries to extend on. I'm not sure it is implementable in a performant way, though. I'd love it if some browsers folks that deal with these code areas can share their opinions on the matter here.

[1] https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ResourcePriorities/Overview.html
Comment 10 Simon Pieters 2013-06-25 15:27:01 UTC
I can see these use cases stated here:

1. Lazy loading of images that are not initially visible.
2. Pausing animations with requestAnimationFrame or CSS animations if an element is not currently visible for better performance or to not drain the user's battery.
3. Removing elements that are no longer visible to release memory.

(1) seems to be what the lazyload="" attribute is intended to address. Is that sufficient?

The ViewportObserver API seems like a bad solution to this use case since images are fetched before scripts run, so it is better to have a declarative way to delay fetching of images. Also, it seems to me that it would be a bad user experience to start fetching an image after it has entered the viewport. It would be better to fetch it before it has entered the viewport. The user agent is probably in a better position than the author to decide when to fetch a lazy image.

(2) Jake Archibald suggests that requestAnimationFrame should handle this with a second parameter. Would that be sufficient? Has that been proposed to the relevant WG?

Pausing CSS animations that are not currently visible seems like something that user agents can do without help from the author.

(3) It seems to me that browsers could optimize this without having the author removing and inserting elements while scrolling. In fact, I can imagine that removing and inserting elements while scrolling can make things worse.

I'm not saying that the proposed feature should not be added. However, there are several proposals that seem to address the stated use cases. We need to evaluate which best address the use cases.