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:

  1. A demo that animates 500 list items as they scroll into view
  2. A demo that covers lazyloading responsive images

At a glance…

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");
        });
    }
});
A barebones example showing how you might lazyload responsive images

The method accepts a configuration Object containing the following attributes:

Attribute Required Description
pattern Yes

A String value representing a CSS Selector1, used when selecting the set of DOM elements to observe.

callback Yes

A function invoked by the script whenever elements that match the CSS Selector pattern have scrolled into the viewport. The function is passed an Array of DOM elements.

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 callback function will not fire a second time.

fastVisibilityCheck No

An optional Boolean value that indicates if the faster algorithm for determining an elements visibility should be used2. Defaults to FALSE.

If you know that none of the observable elements targetted by pattern will be positioned within an overflow:scroll container then set fastVisibilityCheck to TRUE. This tells the script to use a much faster algorithm for checking if an element has been scrolled into view (for the curious, we no longer traverse up the DOM tree testing if any parent element is hidden within a scroll container).

rootMargin No

An optional Integer value representing the pixel margin to use when determining if an element has scrolled into the viewable area. Defaults to 150.

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();
An example showing how to observe dynamically created elements

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]");
An example showing how to unobserve elements

<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!
}
An example showing how to determine browser support

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>
An example showing where to place the script within the markup

“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!
}
The cuts the mustard test used by 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: