JSFiddle - React, Tailwind, and code Playground

by Bryan Braun

HTML

<header class="margin-line"></header>
<div class="spacer"></div>
<div class="triangle"></div>
<div class="spacer"></div>
<div class="spacer"></div>
<div class="spacer"></div>
<div class="controls">
  <dl>
    <dt>Observer top margin:</dt>
    <dd id="margin-readout"></dd>
    <br />
    <dt>Last intersection position:</dt>
    <dd id="intersection-readout"></dd>
  </dl>
  <form action="">
    <input id="range" type="range" min="0" max="100" value="50">
  </form>
</div>

CSS

.margin-line {
  position: fixed;
  top: 50px;
  width: 100%;
  border: 1px solid teal;
}

.spacer {
  width: 100%;
  height: 300px;
  background: antiquewhite;
}

.triangle {
  width: 0;
  height: 0;
  margin: 0 auto;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-bottom: 16px solid red;
}

.controls {
  padding: 8px;
  background: #e8e8e8;
  display: inline-block;
  position: fixed;
  left: 0;
  bottom: 0;
  border: 1px solid #bbb;
  font-family: helvetica, ariel, sans-serif;
  font-size: 11px;
}

dt,
dd {
  display: inline;
}

JavaScript

// Elements
const marginLineEl = document.querySelector('.margin-line');
const rangeEl = document.querySelector('[type="range"]');
const marginReadoutEl = document.querySelector('#margin-readout');
const intersectionReadoutEl = document.querySelector('#intersection-readout');
const triangleEl = document.querySelector('.triangle');

const initialTopMargin = 50;
let observer;

// INTERSECTION OBSERVER CODE
function setupObserver(topMargin) {
  const options = {
    root: null, // defaults to the viewport
    rootMargin: `-${topMargin}px 0px 0px 0px`,
    threshold: 1,
  }

  observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      intersectionReadoutEl.innerHTML = entry.boundingClientRect.top;
    });
  }, options);

  observer.observe(triangleEl);

  // Adjust Readout
  marginReadoutEl.innerHTML = topMargin;
  marginLineEl.style.top = `${event.target.value}px`;
}

setupObserver(initialTopMargin);

// SLIDER CODE
rangeEl.addEventListener('input', (event) => {
  observer.unobserve(triangleEl);
  setupObserver(event.target.value);
});