Skip to content

Instantly share code, notes, and snippets.

@dimfeld
Created May 28, 2020 19:25
Show Gist options
  • Save dimfeld/723aff37ce45cdbf47d0dee8ea9aa1bf to your computer and use it in GitHub Desktop.
Save dimfeld/723aff37ce45cdbf47d0dee8ea9aa1bf to your computer and use it in GitHub Desktop.
Svelte IntersectionObserver action example
/**
* This is intended to be used with the svelte `use` syntax. It examines all
* child elements of the element that match `selector`, and calls the callback
* when the child element with the highest percentage overlap with the visible
* part of the container changes.
* <div use:mostVisibleElement={{ cb: setActive }}><section><section></div>
*/
export default function mostVisibleElement(
container: Element,
{ selector, cb }
) {
if (typeof IntersectionObserver === 'undefined') {
return {};
}
selector = selector || 'section';
let intersections = new Map();
let activeElement;
let intObserver = new IntersectionObserver(
(entries) => {
for (let entry of entries) {
intersections.set(entry.target, entry.intersectionRatio);
}
let maxIntersectedChild;
let maxRatio = 0;
for (let child of children) {
let ratio = intersections.get(child);
if (ratio > maxRatio + 0.1) {
maxRatio = ratio;
maxIntersectedChild = child;
if (ratio === 1) {
break;
}
}
}
if (maxIntersectedChild && maxIntersectedChild !== activeElement) {
activeElement = maxIntersectedChild;
cb(maxIntersectedChild);
}
},
{
root: container,
threshold: [1, 0.75, 0.5, 0.25, 0.1, 0],
}
);
let mutObserver = new MutationObserver(() => update);
mutObserver.observe(container, { childList: true });
let children = [];
var update = () => {
intObserver.disconnect();
intersections.clear();
children = [];
activeElement = null;
container.querySelectorAll(selector).forEach((child) => {
children.push(child);
intObserver.observe(child);
});
};
update();
return {
destroy() {
intObserver.disconnect();
mutObserver.disconnect();
},
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment