A lightweight lazy loader based on Intersection Observer V2 with a tiny fallback for old browsers.
$lazy(
selector, /* string, Node, NodeList or observer config object */
callback /* optional: custom in-view callback for manual lazy loading */
); Documentation is available on docs.style.tools/lazy.
npm install @style.tools/lazy --savecomposer require styletools/lazyThe selector entry accepts multiple configuration formats including a string, an Array, a Node, NodeList or a JSON object with observer configuration.
{
"selector": "[data-src]",
"threshold": 0.006,
"rootMargin": "0px"
}{
"selector": "[data-src]",
"observer": {
"threshold": 0.006,
"rootMargin": "0px",
"trackVisibility": true,
"delay": 100
}
}The array based index config is a compressed format to save size in the HTML document.
- [0] = selector
- [1] = threshold OR observer config when an object
- [2] = rootMargin
["[data-src]", 0.006, "0px"]["[data-src]", {
"threshold": 0.006,
"rootMargin": "0px",
"trackVisibility": true,
"delay": 100
}]When an element enters the viewport the event $lazy is fired on the element. The event contains a reference to the HTML node and the IntersectionObserverEntry.
// listen for $lazy event on any element
$(document)[0].addEventListener('$lazy', console.log);
// selectively listen for $lazy event on images using jQuery
jQuery('img').on('$lazy', console.log);The event data is available via event.detail.
When using a custom callback the $lazy script essentially functions as a simple in-view solution that can be used for many purposes.
$lazy('div#id', function(entries) {
// entries is a Array of `IntersectionObserverEntry` or HTML Nodes
// you need to manually verify if the browser supports Intersection Observer
if (window.IntersectionObserver) {
// entries[0] = IntersectionObserverEntry
// entries[0].target = element
} else {
// entries[0] = element
}
})To enable usage in combination with a strict Content-Security-Policy the script can be configured using a data-l attribute on the script source element.
<script data-l='{
"selector": "[data-src*='cdn.domain.com']",
"observer": {
"threshold": [1.0],
"trackVisibility": true,
"delay": 100
}
}'>
// dist/lazy-data-attr.js (source)
</script>Multiple configurations are supported via the special multi-token ||. The token needs to be included at the begining and each configuration needs to be valid JSON.
||{config...}||{second config...}
$lazy includes a polyfill for IntersectionObserver. It can be automatically loaded when using $async.
<script data-c='[
{
"ref": "lazy",
"src": "dist/lazy-data-attr+polyfill.js",
"attributes": {
"data-l": "[\"selector\", 0.006, \"0px\"]"
},
"load_timing": "domReady",
"cache": "localstorage"
},
{
"ref": "lazy-polyfill",
"src": "dist/intersectionobserver-polyfill.js",
"load_timing": {
"type": "method",
"method": "$lazypoly"
},
"cache": "localstorage"
}
]'>
/* $async IIFE with timing and API module */
</script>In the example, the $async timing method method defines window.$lazypoly which will automatically load the polyfill for browsers that require the polyfill. It uses localStorage for instant loading.
Alternatively, when using $lazy without $async, you can manually define window.$lazypoly with a function that returns a Promise or a object containing a .then method.
window.$lazypoly = function() {
// load polyfill
// ...
return {
then: function(callback) {
// wait until polyfill is loaded and resolve callback
callback();
}
}
};When using $async you can alternatively use window.$lazypoly with a string or a object to pass to $async.js which could load anything.
Alternatively, when including $lazy inline, the data-poly attribute enables to define a string to pass to $async.js.
<script data-l='... lazy config ...' data-poly='... config to pass to $async.js to load polyfill ...'>
// dist/lazy-data-attr+polyfill.js
</script>

