Peekaboo
Fires a one-time event whenever elements of your choice are about to scroll into the viewport. Perfect for triggering animations or for just-in-time asset loading, for example; images, javascript libraries such as twitter and facebook, article comments or 3rd party advertisments.
The more curious amongst you may wish to jump straight to one of the demos:
- A demo that animates 500 list items as they scroll into view
- A demo that covers lazyloading responsive images
At a glance…
- Uses the very fast
IntersectionObserverAPI where possible - Fallsback to using a single
scrollandresizeevent in other browsers but hooks intorequestAnimationFrameto stop scroll jank - Correctly calculates if an observed element is hidden in the overflow of a parent element
- A little over 1k when minified & gzipped
The Peekaboo API
The API was purposely kept small and focused – It contains only two methods, both of which are described below:
<Object> window.peekaboo
An Object window.peekaboo is made available. It makes public the following API methods.
peekaboo.observe(<Object>)
The peekaboo.observe method is used to initiate the observation of DOM elements; for example:
peekaboo.observe({
"pattern": "img[data-srcset]",
"callback": function(elemArray) {
elemArray.forEach(function(elem) {
elem.setAttribute("srcset", elem.getAttribute("data-srcset"));
elem.removeAttribute("data-srcset");
});
}
});
The method accepts a configuration Object containing the following attributes:
| Attribute | Required | Description |
|---|---|---|
pattern |
Yes | A |
callback |
Yes |
A Elements are observed on a one-time-only basis meaning if the element is scrolled out of the observable area and then back in again, the |
fastVisibilityCheck |
No | An optional If you know that none of the observable elements targetted by |
rootMargin |
No | An optional |
Adding observable elements to the DOM dynamically
If no configuration object is passed to the peekaboo.observe method, the script cycles through the list of currently assigned patterns and recalculates the observable element set for each. This is a useful way to update the scripts
internal element list if potential observable elements have been added to the DOM dynamically.
// Add an observable element to the DOM
document.body.appendChild(newObservableElement);
// Now tell peekaboo about it
peekaboo.observe();
peekaboo.unobserve(<String>)
Should you no longer need to observe elements, just call the peekaboo.unobserve method, passing in a String value representing a CSS Selector previously used when calling peekaboo.observe.
peekaboo.unobserve("img[data-srcset]");
<Boolean> peekaboo.supported
A Boolean variable named peekaboo.supported is made available by the script to enable you to determine if the browser is supported.
if(peekaboo && peekaboo.supported) {
// Yeah, start doing cool things!
peekaboo.observe({
"pattern": "pattern",
"callback": callbackFunction
);
} else {
// Yikes, start the backup plan!
}
Browser support
Modern browsers only (IE10+). The script relies on either the IntersectionObserver API or a mix of addEventListener, querySelectorAll, requestAnimationFrame and getBoundingClientRect – which limits it to a more modern browser experience. Hopefully browser uptake for the IntersectionObserver API will be swift and this script will be obsolete in the near future (caniuse: Intersection Observer).
Loading the script
Add the script to the page source code as shown below. It’s a good idea to add it directly before the closing </body> tag as it requires the DOM to be populated before initialisation.
<script src="/the/path/to/peekaboo.min.js"></script>
</body>
“Cuts the mustard” async loading
If you already use an async file loader (such as loadJS) then the following cuts the mustard test can be performed before loading the script:
if(("IntersectionObserver" in window)
||
("requestAnimationFrame" in window
&& "querySelectorAll" in document
&& "addEventListener" in document
&& "getBoundingClientRect" in document.createElement('div'))) {
// Yeah, load the script!
}
This is basically the same test performed internally by the script when setting the peekaboo.supported variable.
This may seem a bit heavy handed but requestAnimationFrame is often polyfilled and the check for IE9+ needed to be as failsafe as possible.
Performance matters
Any script that’s observing DOM elements will undoubtedly demand CPU cycles but the following speed gains have been integrated:
- Uses the very fast
IntersectionObserverAPI where possible - Attempts to reuse the same
IntersectionObserverinstance where possible for all calls topeekaboo.observe - Batches callbacks i.e. one callback for many elements, not one callback per element
- For browsers that don’t support the
IntersectionObserver…- Fallsback to using a single
scrollandresizeevent in other browsers but hooks intorequestAnimationFrameto stop scroll jank - Automatically removes the
scrollandresizeevent handler when no longer needed - Caches element lists to reduce the
querySelectorAlloverhead to a bare minimum when thescroll/resizefallback is used - Caches window
heightandwidthto help avoid layout thrashing - Ships with a
fastVisibilityCheckoption for speeding things up even more
- Fallsback to using a single