4

I am using the IntersectionObserver API.

When a specific section is entered, background-color is changed.
To handle this, I have to obtain the index of an entered entry in the array of IntersectionObserverEntry called entries here.

I used forEach method to get the index of the entry, but it strangely always set index as 0. But when I access entry by obtained index, it works well.

const intersectionObserver = new IntersectionObserver(entries => {
  entries.forEach((entry,index) => {
    if(entry.isIntersecting) {
      console.log('Intersecting',index);
      entry.target.className = entry.target.className.replace('hidden','fadeIn');
      changeBackgroundColor(entry.target.dataset.color);
      changeLogoColor(entry.target.dataset.theme);
    } else {
      console.log('Leave',index);
      entry.target.className = entry.target.className.replace('fadeIn','hidden');
    }
  });
});
const fadeInElemes = document.querySelectorAll('.hidden');
fadeInElemes.forEach((fadeInElem) => intersectionObserver.observe(fadeInElem));

Result is below...

Intersecting 0
Leave 0
Leave 0 .....

What is wrong with my code? Why index is always 0, but access by obtained index results in the right element?

EDIT

Log of entries

Kaiido
  • 123,334
  • 13
  • 219
  • 285
undefined
  • 978
  • 1
  • 12
  • 30
  • IMHO the point of your issue isn't `indexOf()` behavior, try to log the array `entries` – Mosè Raguzzini Jul 31 '19 at 08:10
  • @MosèRaguzzini I edited for that. – undefined Jul 31 '19 at 08:12
  • Ok indexOf is failing because objects are shallow compared and returns only the first item found – Mosè Raguzzini Jul 31 '19 at 08:14
  • @MosèRaguzzini At first, I suspected what you said, but if `indexOf` is not working, I cannot obtain right element for that index. However, it results in right element. Why? – undefined Jul 31 '19 at 08:15
  • 1
    Nope, try to iterate and log foreach second parama (Index), you probably do not need indexOf() – Mosè Raguzzini Jul 31 '19 at 08:16
  • @MohanRamalingam with object comparison it may not work as expected – Mosè Raguzzini Jul 31 '19 at 08:17
  • As you said, I even experimented second parameter of `forEach`, but failed for that too – undefined Jul 31 '19 at 08:18
  • 1
    Because entries array in callback of IntersectionObserver contain only one item and fadeInElemes are setting the observer N-times? So each Intersecting 0, Leave 0, Leave 0 is called by fadeInElems foreach observe call – dKorosec Jul 31 '19 at 08:31
  • @dKorosec You are right, I figured out logging console. Anyway, thx for your comment – undefined Jul 31 '19 at 08:44
  • Can you set up a live example? From the [edit] mode, you should have access to what we call *StackSnippets* from the icon looking like `<>`. We will need a bit of html, a bit of css and a bit of the logic to reproduce your case. But note that to have multiple *entries* in an IntersectionObserver callback, you'd need these elements to cross a threshold at the same exact time. Since here your thresholds are `[0]`, that would mean either they're at the same top position, or they meet at their top and bottom. – Kaiido Jul 31 '19 at 08:45
  • If `index` always is `0`, that just means that `entries.length` is always `1`. – Bergi Jul 31 '19 at 08:52
  • @HaramBae Did you ever figure out why this is happening? Only way I could successfully index is setting ids of elements to index values and using if/switch statements on them. – GHOST-34 Apr 28 '20 at 01:03

5 Answers5

2

You can link index to element's property before register element in observer. Important! Your property name should be unique to avoid overwriting standard properties.

Please read comments below, i have separated the lines of code where the index has been saved (18 line) and readed (5 line).

const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {

        // read index from element’s property
        let index = entry.target.elems_index;

        if (entry.isIntersecting) {
            console.log(index, entry.target, 'is visible');
        } else {
            console.log(index, entry.target, 'is hidden');
        }
    })
});
let elems = document.querySelectorAll('.elem');
elems.forEach((elem, index) => {

    // save index as element’s property before register the element in observer
    elem.elems_index = index;

    observer.observe(elem);
});

Open pen's console there to see result there: https://codepen.io/rafaylik/pen/jOMQQdd

Michael Rafailyk
  • 433
  • 4
  • 11
0

You can access to the index of entry inside your forEach, no need to use indexOf().

entries.forEach((entry, i) => console.log(i))

The MDN web docs is well documented with nice exemples, you can most of the time find what you need there, here is the link for the forEach method

letzam
  • 69
  • 4
0

IndexOf may not work as you expect, when comparing objects:

const user1 = {name : "nerd", org: "dev"};
const user2 = {name : "nerd", org: "dev"};
const eq = user1 == user2;
console.log(eq); // gives false

const myArr = [user1, user2];
console.log(myArr.indexOf(user1)); // gives 0 because same instance
console.log(myArr.indexOf(user2)); // gives 1 because same instance

const myArr2 = [{name : "nerd", org: "dev"}, {name : "nerd", org: "dev"}];
console.log(myArr2.indexOf(user1)); // gives -1 because not same instance
console.log(myArr2.indexOf(user2)); // gives -1 because not same instance

If IndexOf() is returnig 0 it means current entry instance is the same as the one in position 0.

Take a look at how do you create your array, maybe entries contains the same instance across the array.

Mosè Raguzzini
  • 15,399
  • 1
  • 31
  • 43
  • Even at [rev 2](https://stackoverflow.com/revisions/57286010/2) where they were indeed using indexOf, your answer wasn't making any sense. `forEach((el, i, arr) => arr.indexOf(el) === i)` will always only produce `true` if there is no duplicate entries, which is impossible in the callback of an IntersectionObserver – Kaiido Jul 31 '19 at 09:01
  • @Kaiido `Array.indexOf()` returns either *the index of the first found element* or *-1*. It do not returns a boolean, mate. – Mosè Raguzzini Jul 31 '19 at 09:14
  • I refer to console.log('Leave',entries.indexOf(entry)), always returning 0 – Mosè Raguzzini Jul 31 '19 at 09:18
  • Because it always returns 0 in console.log, at every iteration – Mosè Raguzzini Jul 31 '19 at 09:30
  • Mhaa... if it returns 0 its because it's been found, at index 0. So your answer saying that their object is not found because two object literals are not same objects is completely off. If you were somehow right, then they would have -1. – Kaiido Jul 31 '19 at 09:32
0

you are logging the index from entries not from element array

fadeInElemes.forEach((fadeInElem) => intersectionObserver.observe(fadeInElem));

this is where you get the index you actually looking for

0

You can check the entry target against the observed elements. So something like this:

const elementList = document.querySelectorAll('.list-of-elements')

const observer = new IntersectionObserver((entries) => {
  for(const entry of entries) {
    if (entry.isIntersecting) {
      // compare target to element list
      const currentIndex = elementList.indexOf(entry.target)
    }
  }
})

Array.from(elementList).forEach(element => observer.observe(element))
Jmorel88
  • 66
  • 2
  • 12