intersectionobserver
// super class that can be mixed into any JS class based object
// in order to add an intersection / visibility observer to it
// example code is for adding to a web component (LitElement optimized)
const IntersectionObserverMixin = function (SuperClass) {
// SuperClass so we can write any web component library / base class
return class extends SuperClass {
/**
* Constructor
*/
constructor() {
super();
// listen for this to be true in your element
this.elementVisible = false;
// threasholds to check for, every 25%
this.IOThresholds = [0.0, 0.25, 0.5, 0.75, 1.0];
// margin from root element
this.IORootMargin = "0px";
// wait till at least 50% of the item is visible to claim visible
this.IOVisibleLimit = 0.5;
// drop the observer once we are visible
this.IORemoveOnVisible = true;
// delay in observing, performance reasons for minimum at 100
this.IODelay = 100;
}
/**
* Properties, LitElement format
*/
static get properties() {
let props = {};
if (super.properties) {
props = super.properties;
}
return {
...props,
elementVisible: {
type: Boolean,
attribute: "element-visible",
reflect: true,
},
};
}
/**
* HTMLElement specification
*/
connectedCallback() {
if (super.connectedCallback) {
super.connectedCallback();
}
// setup the intersection observer, only if we are not visible
if (!this.elementVisible) {
this.intersectionObserver = new IntersectionObserver(
this.handleIntersectionCallback.bind(this),
{
root: document.rootElement,
rootMargin: this.IORootMargin,
threshold: this.IOThresholds,
delay: this.IODelay,
}
);
this.intersectionObserver.observe(this);
}
}
/**
* HTMLElement specification
*/
disconnectedCallback() {
// if we have an intersection observer, disconnect it
if (this.intersectionObserver) {
this.intersectionObserver.disconnect();
}
if (super.disconnectedCallback) {
super.disconnectedCallback();
}
}
/**
* Very basic IntersectionObserver callback which will set elementVisible to true
*/
handleIntersectionCallback(entries) {
for (let entry of entries) {
let ratio = Number(entry.intersectionRatio).toFixed(2);
// ensure ratio is higher than our limit before trigger visibility
if (ratio >= this.IOVisibleLimit) {
this.elementVisible = true;
// remove the observer if we've reached our target of being visible
if (this.IORemoveOnVisible) {
this.intersectionObserver.disconnect();
}
}
}
}
};
};