We have a couple of configuration variables, then we grab the elements we want to observe, and pass them to a createObserver function.

In createObserver , we:

Set options for the observer. We’ll stick with the defaults except to provide an array of thresholds from 0 to 1.0. Our callback function will be called each time the intersection ratio for an observed element crosses a threshold in this array. Create the observer, passing in the callback function, handleIntersection , along with the options . Deploy the observer on each of the elements we want to observe.

Using the intersection ratio

Now, we can put our newfound intersection data to use. First, with the intersectionRatio , we could fade each img in and out based on what percentage of it is in view:

function handleIntersection(changes) {

changes.forEach((change) => {

let figure = change.target;

let img = figure.getElementsByTagName("img")[0];

let ratio = change.intersectionRatio;

img.style.opacity = minOpacity + (1 - minOpacity) * ratio;

});

}

This will fade the image in from the minimum opacity we set, to full opacity. With transition: opacity 500ms; applied, we get a nice smooth transition.

I’d also like to hide the caption when the image is in full view, like so:

let caption = figure.getElementsByTagName('figcaption')[0]; if (ratio < 0.9) {

caption.style.opacity = 1;

} else {

caption.style.opacity = 0;

}

The gallery now looks like this.

Using the intersection timestamp

One frequently-used stat in site analytics is time on page, but with many simpler sites foregoing multiple pages in favor of a longer, scrolling page, the Intersection Observer API could help us track the time users spend on each section. In our example, let’s say we want to know how long users spend gazing at each of our photos.

We’ve got the time at which each intersection change occurred. So, we could figure out how long an observed element is within an intersection threshold. In our simple case — how long is each figure visible in the browser viewport?

We can add to the above:

if(ratio > .8 && figure.dataset.startTime === undefined){

figure.dataset.startTime = entry.time;

}

if(ratio < .8 && figure.dataset.startTime !== undefined){

console.log(entry.time - figure.dataset.startTime)

delete figure.dataset.startTime;

}

I’ve picked .8, or 80% visible, as the threshold I’ll count as “in view”. We’re just storing the time the figure first crosses the .8 threshold in a data attribute called startTime on the target element. When it crosses this threshold a second time, we console.log the difference, and then delete the startTime data attribute. (Probably you wouldn’t want to be adding and removing data attributes on hundreds of elements. Alternatively, you could save the data in an object).

Of course, logging to the console isn’t too interesting, so instead you could send the timing data to an analytics tool. For example:

if(ratio > .8 && figure.dataset.startTime === undefined){

figure.dataset.startTime = change.time;

}

if(ratio < .8 && figure.dataset.startTime !== undefined){

ga('send', {

hitType: 'timing',

timingCategory: 'Images',

timingVar: 'view_time',

timingValue: change.time - figure.dataset.startTime,

timingLabel: figure.getElementsByTagName('img')[0].getAttribute('src')

});

delete figure.dataset.startTime;

}

A separate “in view” time will be recorded each time the figure goes above and then below the 80% threshold, so we would have some aggregating to do on the reporting side.

Using the bounding rectangles

I actually can’t think of a use-case for these. If you come up with one, leave a comment below! I did this, but I don’t think it counts as useful.

Using isIntersecting

If all we need to know is whether the target element is intersecting the root, we can use the Boolean isIntersecting . On to lazy loading!

Lazy loading with the Intersection Observer API

We’ll change our markup slightly to accomplish lazy-loading, using placeholder images and data attributes, and adding an unveil class:

<img src="img/placeholder-v.png" class="unveil"

data-src="img/black-trumpet-700.jpg"

data-srcset="img/black-trumpet-1400.jpg 1400w, img/black-trumpet-2800.jpg 2800w"

alt="Top view of a delicate light gray-brown, funnel-shaped mushroom amongst leaf litter on the forest floor."

/>