JSFiddle - React, Tailwind, and code Playground

Intersection Observer test: scrolling with position announcement

by Travis Almand

HTML

<div id="container">
  <div id="scroller">
    <div id="contained"></div>
  </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="reset">reset</button>
    <button id="horizontal">horizontal</button>
    <button id="vertical">vertical</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: block;
  height: 400px;
  overflow: auto;
  justify-content: center;
  width: 400px;
  
  &.horizontal #scroller {
    height: 200px;
    margin-top: 100px;
    width: 1200px;
  }
  &.vertical #scroller {
    height: 1200px;
    margin-left: 100px;
    width: 200px;
  }
}
#scroller {
  align-items: center;
  display: flex;
  height: 400px;
  justify-content: center;
  width: 400px;
}
#contained {
  background-color: rebeccapurple;
  height: 200px;
  width: 200px;
}


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

JavaScript

console.clear();

let container = document.querySelector('#container');
let contained = document.querySelector('#contained');
let isIntersecting = document.querySelector('#intersecting span');
let intersectionRatio = document.querySelector('#ratio span');
let position = document.querySelector('#position span');
let reset = document.querySelector('#reset');
let horizontal = document.querySelector('#horizontal');
let vertical = document.querySelector('#vertical');

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

reset.addEventListener('click', function () {
	container.classList.remove('horizontal', 'vertical');
});
horizontal.addEventListener('click', function () {
	container.classList.add('horizontal');
  container.classList.remove('vertical');
});
vertical.addEventListener('click', function () {
	container.classList.remove('horizontal');
  container.classList.add('vertical');
});

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