JSFiddle - React, Tailwind, and code Playground
Vue + intersection observers, parent has observer and passes data down to component through a prop
by Travis Almand
HTML
<div id="app">
<section-a class="full-height" data-example="example-1" :entry="entries['example-1']"></section-a>
<section-a class="half-height" data-example="example-2" :entry="entries['example-2']"></section-a>
<section-a class="full-height" data-example="example-3" :entry="entries['example-3']"></section-a>
<section-a class="half-height" data-example="example-4" :entry="entries['example-4']"></section-a>
<section-a class="full-height" data-example="example-5" :entry="entries['example-5']"></section-a>
</div>
CSS
.full-height {
background-color: #E0E0E0;
border: 1px solid black;
min-height: 100vh;
transition: 0.5s;
}
.half-height {
background-color: #E0E0E0;
border: 1px solid black;
min-height: 50vh;
transition: 0.5s;
}
.sticky {
left: 0;
padding: 10px;
position: sticky;
top: 0;
}
.isIntersecting {
background-color: #663399;
color: #fff;
}
JavaScript
console.clear();
Vue.component('section-a', {
template: `
<div :class="{isIntersecting: entry.isIntersecting}">
<div class="sticky">{{ entry.isIntersecting }} {{ entry.intersectionRatio }}</div>
</div>`,
props: {
entry: {
type: Object,
default: () => {
return {
isIntersecting: Boolean,
intersectionRatio: Number
}
}
}
}
});
new Vue({
el: "#app",
data () {
return {
io_options: {
root: null,
rootMargin: '0px',
threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
},
observer: null,
entries: {}
}
},
methods: {
io_callback: function (entries, observer) {
entries.forEach(entry => {
this.$set(this.entries, entry.target.dataset.example, {isIntersecting: entry.isIntersecting, intersectionRatio: entry.intersectionRatio});
});
}
},
mounted () {
this.observer = new IntersectionObserver(this.io_callback, this.io_options);
document.querySelectorAll('[data-example]').forEach(example => {
this.observer.observe(example);
});
}
})