JSFiddle - React, Tailwind, and code Playground

Intersection Observer test with animated element

by Travis Almand

HTML

<input type="range" id="horizontal" min="-350" max="350" value="0">

<div id="intersectionRect" class="hide"></div>

<div id="container">
  <div id="contained"></div>
</div>

<div id="panel">
  <div id="intersecting">isIntersecting: <span></span></div>
  <div id="ratio">intersection ratio: <span></span></div>
  <div class="buttons">
    <button id="animate">animate</button>
    <button id="hidden">hidden</button>
    <button id="intersection">intersection</button>
    <button id="absolute">absolute</button>
    <button id="opacity">opacity</button>
  </div>
</div>

SCSS

body {
  align-items: center;
  background-color: #E0E0E0;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
  justify-content: center;
}

#intersectionRect {
  background-color: #307B21;
  position: absolute;
  z-index: 10;
  
  &.hide {
    display: none !important;
  }
}

#container {
  align-items: center;
  background-color: #fff;
  border: 3px solid rebeccapurple;
  display: flex;
  height: 400px;
  justify-content: center;
  width: 400px;
  
  &.hidden {
    overflow: hidden;
  }
}
#contained {
  background-color: rebeccapurple;
  height: 200px;
  width: 200px;
  
  &.animate {
    animation: sidetoside 5s linear infinite alternate;
  }
  &.absolute {
    position: absolute !important;
  }
  &.opacity {
    opacity: 0 !important;
  }
}
#panel {
  margin: 10px;
  width: 400px;
  
  .buttons {
    margin: 10px 0;
  }
}
#horizontal {
  width: 400px;
}

@keyframes sidetoside {
  0% {
    transform: translate3d(-350px, 0, 0);
  }
  100% {
    transform: translate3d(350px, 0, 0);
  }
}

JavaScript

console.clear();

let sliderH = document.querySelector('#horizontal');
let container = document.querySelector('#container');
let contained = document.querySelector('#contained');
let isIntersecting = document.querySelector('#intersecting span');
let intersectionRatio = document.querySelector('#ratio span');
let animate = document.querySelector('#animate');
let hidden = document.querySelector('#hidden');
let intersection = document.querySelector('#intersection');
let absolute = document.querySelector('#absolute');
let opacity = document.querySelector('#opacity');
let intersectionRect = document.querySelector('#intersectionRect');

let threshold = [...Array(100).keys()].map(x => x / 100);
let options = {
	root: container,
  rootMargin: '0px',
  threshold: threshold
}

let observer = new IntersectionObserver(callback, options);
observer.observe(contained);

sliderH.addEventListener('input', function (e) {
	contained.style.transform = `translate3d(${e.target.value}px, 0, 0`;
});

animate.addEventListener('click', function () {
	contained.classList.toggle('animate');
});
hidden.addEventListener('click', function () {
	container.classList.toggle('hidden');
});
intersection.addEventListener('click', function () {
	intersectionRect.classList.toggle('hide');
});
absolute.addEventListener('click', function () {
	contained.classList.toggle('absolute');
});
opacity.addEventListener('click', function () {
	contained.classList.toggle('opacity');
});

function callback (entries, observer) {
	entries.forEach(entry => {
  	isIntersecting.innerText = entry.isIntersecting;
    intersectionRatio.innerText = entry.intersectionRatio;
    
    intersectionRect.style.display = entry.intersectionRatio > 0.99 ? 'none': 'block';
    intersectionRect.style.height = entry.intersectionRect.height + 'px';
    intersectionRect.style.width = entry.intersectionRect.width + 'px';
    intersectionRect.style.top = entry.intersectionRect.top + 'px';
    intersectionRect.style.left =...