<div class="page">
<div class="sentinal"></div>
<h1 class="header">Intersection observer <br/>& position: sticky
</h1>
<ul class="Items">
<li class="Item">
<div class="Item_Img"></div>
<div class="Item_Inner">
<h2>Interaction Observer is watching the pink bar at the top of the screen.</h2>
</div>
</li>
<li class="Item">
<div class="Item_Img"></div>
<div class="Item_Inner">
<h2>As soon as it enters or leaves the viewport, an event is triggered.</h2>
</div>
</li>
<li class="Item">
<div class="Item_Img"></div>
<div class="Item_Inner">
<h2>The event contains an 'isIntersecting' value - which tells us if the pink bar is/isn't in the viewport.</h2>
</div>
</li>
<li class="Item">
<div class="Item_Img"></div>
<div class="Item_Inner">
<h2>We can use this event to do whatever we need, in this case, just toggling a class on the h1.</h2>
</div>
</li>
<li class="Item">
<div class="Item_Img"></div>
<div class="Item_Inner">
<h2>Unrelated to Intersection Observer, but also handy, 'position: sticky' is used on the h1.</h2>
</div>
</li>
<li class="Item">
<div class="Item_Img"></div>
<div class="Item_Inner">
<h2>This can all be accomplished without any scroll event listeners.</h2>
</div>
</li>
</ul>
</div>
.sentinal {
// DOM element for triggering the intersection observer event
// you can use 'height' to determine when the event is triggered
height: 3rem;
position: absolute;
top: 0;
left: 0;
right: 0;
background-color: #F10C49;
}
body {
font-family: BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica", "Open Sans", "Arial", sans-serif;
background-color: #f3eee8;
}
.page {
padding: 6rem 0;
max-width: 600px;
margin: 0 auto;
}
.header {
background-color: white;
position: sticky;
position: sticky;
top: 2rem;
padding: 1rem;
line-height: 1.5;
box-decoration-break: clone;
z-index: 1;
transition: all 300ms;
will-change: color, background-color;
&.enabled {
color: white;
background-color: #A70267;
}
}
h1 {
display: inline;
color: #F10C49;
box-decoration-break: clone;
}
* {
padding: 0;
margin: 0;
}
.Items {
color: #FB6B41;
padding-top: 2rem;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-gap: 2rem 3rem;
}
.Item {
display: flex;
flex-direction: column;
}
.Item_Img {
position: relative;
background-color: #EAE4E0;
&::before {
content: '';
position: relative;
padding-bottom: 56.25%;
display: block;
}
}
.Item_Inner {
padding: 4rem 1.5rem 1.5rem;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
background-color: white;
justify-content: flex-end;
min-height: 8rem;
}
View Compiled
const headerEl = document.querySelector('.header')
const sentinalEl = document.querySelector('.sentinal')
const handler = (entries) => {
console.log(entries)
// entries is an array of observed dom nodes
// we're only interested in the first one at [0]
// because that's our .sentinal node.
// Here observe whether or not that node is in the viewport
if (!entries[0].isIntersecting) {
headerEl.classList.add('enabled')
} else {
headerEl.classList.remove('enabled')
}
}
// create the observer
const observer = new window.IntersectionObserver(handler)
// give the observer some dom nodes to keep an eye on
observer.observe(sentinalEl)
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.