kyija
kyija

Reputation: 73

Why is my intersectionObserver returning my querySelect as undefined?

I am attempting to target a parent element and a child element using an intersectionObserver, then I have a function changing the background of the parent to a different color and rotating the child element.

This code works on the parent div, however the child item returns as undefined. Am I unable to target child elements with querySelector, or is the intersectionObserver unable to observe more than one element?

let options = {
  threshold: 0.25
}

let observer = new IntersectionObserver(function(entries, observer) {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      return;
    } else {
      console.log(entry.target);
      console.log(entry.sticky);
      alert('INTERSECTING!');
      entry.target.classList.toggle("red");
      entry.sticky.classList.toggle("rotate");
    }
  });
}, options);

let target = document.querySelector('.placeholder__div__large');
let sticky = document.querySelector('.sticky__container');

observer.observe(target, sticky);
.placeholder__div__large {
  height: 200vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  background: black;
  transition: 2s;
}

.sticky__container {
  position: sticky;
  top: 100px;
  width: 200px;
  height: 200px;
}

.sticky__item {
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: white;
  color: black;
  width: 100%;
  height: 100%;
}

.red {
  background: red;
  transition: 2s;
}

.rotate {
  transform: rotate(180deg);
}
<div class="placeholder__div__large">
  <div class="sticky__container">
    <div class="sticky__item">STICKY ITEM</div>
  </div>
</div>

Upvotes: 1

Views: 818

Answers (2)

FZs
FZs

Reputation: 18619

You can't observe multiple elements by passing them all to .observe, you have to call it multiple times.

Also, I assume you rather wanted to do it like this (I'm not sure if I'm right, but parts of your code didn't make any sense to me):

let options = {
  threshold: 0.25
}

const observer = new IntersectionObserver(function(entries, observer) {
  entries.forEach(entry => {
    console.log('INTERSECTING with', entry.target, entry.isIntersecting);
    entry.target.classList.toggle("intersect", entry.isIntersecting);
  });
}, options);

const target = document.querySelector('.placeholder__div__large');
const sticky = document.querySelector('.sticky__container');

observer.observe(target);
observer.observe(sticky);
.placeholder__div__large {
  height: 200vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  background: black;
  transition: 2s;
}

.sticky__container {
  position: sticky;
  top: 100px;
  width: 200px;
  height: 200px;
}

.sticky__item {
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: white;
  color: black;
  width: 100%;
  height: 100%;
}

.red-whenintersect.intersect {
  background: red;
  transition: 2s;
}

.rotate-whenintersect.intersect {
  transform: rotate(180deg);
}
<div class="placeholder__div__large red-whenintersect">
  <div class="sticky__container">
    <div class="sticky__item rotate-whenintersect">STICKY ITEM</div>
  </div>
</div>

Upvotes: 2

A Haworth
A Haworth

Reputation: 36573

You want to observe the element that is sticky and if it overlaps (within the threshold) of the parent element you want to change sticky by rotating it and the parent to background red.

So, to set up the observer we want to observe sticky. We don't need to observe both elements. Also we want to set a threshold so this is where the options come in:

observer.observe(sticky, options)

In the callback function, we know that the entry will be sticky because that's the only one we're observing so it's safe to have it as you have - if isIntersecting then make the changes. You already have the two elements target and sticky set up in the JS so use those to make the changes, you don't need to refer to entry to find them (and entry.sticky makes no sense anyway).

I am not absolutely sure what the ultimate aim is as the changes are only taking place on an intersection, i.e. not going back to the original on non-intersection, but here's a snippet with the changes to color and rotation as set up in the question:

let options = {
  threshold: 0.25
}

let observer = new IntersectionObserver(function(entries, observer) {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      return;
    } else {
      target.classList.toggle("red");
      sticky.classList.toggle("rotate");
    }
  });
}, options);

let target = document.querySelector('.placeholder__div__large');
let sticky = document.querySelector('.sticky__container');

observer.observe(sticky);
.placeholder__div__large {
  height: 200vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  background: black;
  transition: 2s;
}

.sticky__container {
  position: sticky;
  top: 100px;
  width: 200px;
  height: 200px;
}

.sticky__item {
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: white;
  color: black;
  width: 100%;
  height: 100%;
}

.red {
  background: red;
  transition: 2s;
}

.rotate {
  transform: rotate(180deg);
}
<div class="placeholder__div__large">
  <div class="sticky__container">
    <div class="sticky__item">STICKY ITEM</div>
  </div>
</div>

Upvotes: 0

Related Questions