A JavaScript Intersection Observer Example
I'm working on alice.alanwsmith.com. When it's done, it'll have the full text of Alice's Adventures in Wonderland where every letter gets it's own color and font shape that changes over time.
I've got the page doing what I want, but it's sluggish. I'm not surprised. It's constantly adjusting the size and shape of the entire text. Things smooth out when I remove everything except a single paragraph. I'm thinking I can use Intersection Observer to limit what's being changed to keep things from bogging down.
This page is where I'm experimenting with how to make that happen.
Show Me The Codes
Here's the JavaScript I'm using. It's live and powering the example below let.
let options = ;
let observer = ;
document
.
.;
The code is pretty much a straight extraction from the MDN guidemdn. It's been slimmed down to remove options that did nothing but set the same values as the defaults.
The only difference between this is what I'm using on the Alice site is that I switched the options. value from the document. to null. Doing that sets the browser's viewport as the boundries for the observer instead of an individual element. The changes happen when things move in and out of the window itself.
More Code, Please
Here's the demo HTML for this page and it's output. A couple quick notes about it.
-
The only thing that's required is the
element and thetags it wraps. The rest of the HTML is only there to shim a simulated viewport for the demo with the ability to see what's scrolled above and below itvisual. -
The content of the
tags is random gibberish that's created when they load in. I do that to make that HTML easier to look at.
HTML
scrolling viewport
above viewport
below viewport
<!-- START HERE -->
<!-- AND, WE'RE DONE -->
Output
I'm doing a bunch of stuff in the demo to highlight when and where things change, but the CSS comes down to this for the basic functionality to change the text color:
CSS
}
What Goes Up
Each paragraph has a yellow border at its upper and lower edges. As soon as one of those edges crosses into the viewport the Intersection Observer fires and applies the class if any part of the element is inside the viewport.
If the element is completely outside the view port (i.e. both it's yellow lines are above or below the viewport), then the class is removed.
A Marginal Upgrade
Because the Alice page works with transitions I need to make sure each paragraph of text starts getting updated before it enters the viewportithink.
I'm doing this by adding a rootMargin to the options setup. That changes this:
let options = ;To this:
let options = ;The values for rootMargin work the same way as CSS marginmargin, but they can only be pixels or percentages. They define how far outside the viewport the observer looks for element edgespx.
Flip on the options.rootMargin: "50px 0px" option in the Configuration Options under the example to see it in practice. (The option also turns on two overlay lines to show where they take effect outside the viewport.)
Back Down The Rabbit-Hole
Now, back to the other site to see if this actually does the thing I think it will. Wish me luck,
-a
Post-Script
If you're interested in the JavaScript and CSS that power the visuals for the example, they're here:
Click Me For The CSS
CSS
}
}
}
}
}
}
}
}
}
}
}
}
Click Me For The JavaScript
const optionsHTML = document.;
optionsHTML. = `
<h4>Configuration Options</h4>
<div>
<label>
<input
type="radio"
name="control"
data-send="switchObserver|toggleMargin"
data-param="noRootMargin"
checked>
<code>options.rootMargin</code> is not set
</label>
</div>
<div>
<label>
<input
type="radio"
name="control"
data-send="switchObserver|toggleMargin"
data-param="showRootMargin">
<code>options.rootMargin: "50px 0px"</code>
</label>
</div>`;
const optionsContent = optionsHTML.;
window. =
let observerForStatus = ;
document
.
.;
If you're not familiar with bitty, it's what I'm using for the main interactions.
References
Footnotes
I'm using let for let options = and let observer instead of const options = and const observer so I and change them for the example. I'd use const if I didn't need to do that.
I've got plans to set up so I can hide the wrapping code. That'll make the examples cleaner. Just haven't gotten to it yet.
At least, I think I do. Still gotta test exactly how switching things on and off will work.
You could do 50px for every direction, 50px 50px 50px 50px to explicitly set the top, right, bottom, and left edges, or, the 50px 0px format I'm using here that sets the top and bottom to 50 and the sides to 0.