IntersectionObserver method not working - Javascript

The Chewy picture The Chewy · Feb 7, 2018 · Viewed 13.7k times · Source

I have a div that I want to change color when it scrolls into the viewport, and I'm trying to achieve this with the new intersectionObserver method. I've set my parameters in the config callback, but I can't seem to get the observer itself to add the class to change the background color?

Any help would be amazing.

codepen: https://codepen.io/emilychews/pen/mXVBVK

Answer

Sebastian Simon picture Sebastian Simon · Feb 7, 2018

The function inside the IntersectionObserver constructor is called whenever an intersection changes. You can’t put observer.observe(box); inside it.

Also, item isn’t a DOM element — it’s an IntersectionObserverEntry, so you can’t use .classList on it. You probably meant to address item.target.

Even if the above is corrected, your CSS won’t change, because you’ve used the #box selector to set the background to blue, which has a higher specificity than .active. An easy fix is to change #box to .box and as HTML use <div id="box" class="box"></div> instead.

The corrected code would then look like this:

const config = {
    root: null, // Sets the framing element to the viewport
    rootMargin: "0px",
    threshold: 0.5
  },
  box = document.getElementById("box"),
  observer = new IntersectionObserver((entries) => entries
    .forEach(({target: {classList}}) => classList.add("active")), config);

observer.observe(box);
body {
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 300vh;
}
.box {
  width: 100px;
  height: 100px;
  background: blue;
}
.active {
  background: red;
}
<div id="box" class="box"></div>

Now you need some logic inside the callback:

entries.forEach(({target: {classList}, intersectionRatio}) => {
  if(intersectionRatio > 0.5){
    classList.add("active");
  }
  else{
    classList.remove("active");
  }
});

This will make the <div> red when more than 50 % of it are visible.

const config = {
    root: null, // Sets the framing element to the viewport
    rootMargin: "0px",
    threshold: 0.5
  },
  box = document.getElementById("box"),
  observer = new IntersectionObserver((entries) => entries
    .forEach(({target: {classList}, intersectionRatio}) => {
      if(intersectionRatio > 0.5){
        classList.add("active");
      }
      else{
        classList.remove("active");
      }
    }), config);

observer.observe(box);
body {
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 300vh;
}
.box {
  width: 100px;
  height: 100px;
  background: blue;
}
.active {
  background: red;
}
<div id="box" class="box"></div>