Use IntersectionObserver to detect div-position change

Viewed 477

I want to observe the position of a div element that moves inside a larger container:

The page contains one div with the ID #actual which starts at a known position. Over time, the #actual div changes its position, and I need to run a function when that happens.

The #actual position can change in different ways - via CSS left/top changes, a CSS transform on a parent element, or a sibling changes size and pushes the div up/down.

My current attempt is, to add a hidden div to the page, with the ID #known which starts off at the same position as #actual. Now I try to add an IntersectionObserver that fires when the intersection between #actual and #known changes.

Here's my sample code:

const known = document.querySelector('#known');
const actual = document.querySelector('#actual');

const ioOptions = {
  tolerance: 0,
  root: known
};
const ioObserver = new IntersectionObserver(ioChange, ioOptions);

function ioChange(entries) {
  console.log('Intersection changed:', entries[0].intersectionRect);
}

ioObserver.observe(actual);

Problem

The observer never fires. Why? How can I get the Observer to work?

Or is there a better/different way to detect position changes of #actual?


Click below to see the full sample with the two divs:

const known = document.querySelector('#known');
const actual = document.querySelector('#actual');

// -- Intersection Observer --------

const ioOptions = {
  tolerance: 0,
  root: known
};
const ioObserver = new IntersectionObserver(ioChange, ioOptions);

function ioChange(entries) {
  console.log('Intersection changed:', entries[0].intersectionRect);
}

ioObserver.observe(actual);

// -- Sample animation logic -----

// I expect that this animation constantly triggers the 
// ioChange callback above.

// -- Sample animation logic -----

function setKnown() {
  known.style.left = actual.style.left;
  known.style.top = actual.style.top;
}

function moveBox() {  
  actual.style.left = (Math.random() * 240) + 'px';
  actual.style.top = (Math.random() * 240) + 'px';
}

actual.style.left = '120px';
actual.style.top = '120px';
setKnown();
setInterval(moveBox, 1000);
#root {
  position: relative;
  margin: 20px auto;
  background: #fff;
  border: 1px solid #000;
  width: 320px;
  height: 320px;
  box-shadow: 0 0 30px #0002;
  box-sizing: border-box;
  font: 14px sans-serif;
}

.box {
  width: 80px;
  height: 80px;
  position: absolute;
  text-align: center;
  line-height: 80px;
  white-space: nowrap;
  box-sizing: border-box;
}

#known {
  background: #8881;
  border: 1px dashed #999;
}

#actual {
  background: #0068;
  color: #fff;
  transition: all 0.3s;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>

  <div id="root">
    <div id="known" class="box">Known</div>
    <div id="actual" class="box">Actual</div>
  </div>

</body>

</html>

1 Answers

I know it's an old question, but if anyone happened to come across it, just make small changes to make the example work. Instead of tolerance, you should use the treshold option exactly like this:

const ioOptions = {
  treshold: 0,
  root: known
};

const ioObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if(entry.isIntersecting){
     ioChange(entry);
    }
  });
});

Working example

Related