I have seen many articles on the subject of lazy loading. I think most of the simple approaches struggle if you need 60FPS animations, smooth scrolling, precise control and good browser support. So I would like to share this approach with you — it’s been developed over a few years working on the beoplay.com website and some other projects of mine. But first a brief background:
The problem with one very common approach is to reflow the document on every scroll (which could be 60 times per second), for instance by calling “getBoundingClientRect” or similar. Reflowing is something you want to keep to an absolute minimum. Other modern solutions include IntersectionObserver, but these are limited in flexibility and browser support.
The approach I am sharing here is good if you want and have control. There is one major downside though: you must know when the position (or height) of previous elements change (if this affects your element) — because there will be no reflow or IntersectionObserver alerting you. But if you have control of your modules and you use containers with a css controlled height (get familiar with the padding trick for empty image containers), then this approach is great, it’s been a perfect solution for the many modules and microsites I did for B&O Play.
Let’s get to it:
First we need a global object to hold screen size variables. I’m not talking about 100vh, because the mobile/tablet implementation of this is problematic, but a reliable value that only changes if the device is rotated or if the browser is actually resized.
Secondary we need a module template. This have settings for controlling when to be activated (preload, prestart, start, stop). The reason for this is that sometimes you want assets to load only when x% into the viewport — or maybe the opposite: you want assets to load just before the module enters the viewport, so graphics are ready when scrolled into view. You can extent this in many ways depending on your project’s needs. Sometimes I need a value telling my module (or elements inside it) how many percent it sits from the center of the viewport (for parallax effects and similar) and sometimes you need to start/stop animations at specific positions. In this example I included a parallax method.
The module is created inside a test page (somePage).
Another (but important) benefit with this setup, is avoiding layout thrashing - not just on scroll, but also when the browser resizes or when initializing a page or module. This is done by keeping the read (resize) and write (LayoutUpdate) methods completely separated.
So here’s the CodePen demo. You’re welcome :-)