5

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

const config = {
  root: null,  // sets the framing element to the viewport
  rootMargin: '0px',
  threshold: 0.5
};

const box = document.getElementById('box');

let observer = new IntersectionObserver(function(entries) {
  observer.observe(box);
  entries.forEach(function(item){
    item.classList.add("active");
  });
}, config);
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"></div>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
pjk_ok
  • 618
  • 7
  • 35
  • 90

1 Answers1

21

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 50 % or more of it is visible. The observer will stop firing after 50 % or more is visible, so in some cases intersectionRatio will be exactly 0.5.

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>
Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75