JSFiddle - React, Tailwind, and code Playground

Intersection Observer test with position announcement

by Travis Almand

HTML

<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 id="position">position: <span></span></div>
  
  <div class="buttons">
    <button id="side">side to side</button>
    <button id="up">up and down</button>
  </div>
</div>

SCSS

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

#container {
  align-items: center;
  background-color: #fff;
  border: 3px solid rebeccapurple;
  display: flex;
  height: 400px;
  overflow: hidden;
  justify-content: center;
  width: 400px;
}
#contained {
  background-color: rebeccapurple;
  height: 200px;
  width: 200px;
  
  &.side {
    animation: sidetoside 5s linear infinite alternate;
  }
  &.up {
    animation: upanddown 5s linear infinite alternate;
  }
}

#panel {
  margin: 10px;
  width: 400px;
  
  .buttons {
    margin: 10px 0;
  }
}

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

JavaScript

console.clear();

let container = document.querySelector('#container');
let contained = document.querySelector('#contained');
let side = document.querySelector('#side');
let up = document.querySelector('#up');
let isIntersecting = document.querySelector('#intersecting span');
let intersectionRatio = document.querySelector('#ratio span');
let position = document.querySelector('#position span');

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);

side.addEventListener('click', function () {
	contained.classList.remove('up');
	contained.classList.toggle('side');
});
up.addEventListener('click', function () {
	contained.classList.remove('side');
	contained.classList.toggle('up');
});

function callback (entries, observer) {
	entries.forEach(entry => {
  	let ratio = entry.intersectionRatio;
  	let boundingRect = entry.boundingClientRect;
    let intersectionRect = entry.intersectionRect;
  
  	isIntersecting.innerText = entry.isIntersecting;
    intersectionRatio.innerText = entry.intersectionRatio;
    
    if (ratio === 0) {
    	position.innerText = 'outside';
    } else if (ratio < 0.99) {
      if (boundingRect.left < intersectionRect.left) {
        position.innerText = 'on the left';
      } else if (boundingRect.right > intersectionRect.right) {
        position.innerText = 'on the right';
      } else if (boundingRect.top < intersectionRect.top) {
      	position.innerText = 'on the top';
      } else {
      	position.innerText = 'on the bottom';
      }
    } else {
    	position.innerText = 'inside';
    }
  });
}