<template>
  <div id="app">
    <ul ref="list">
      <li
        v-for="({ elRef, isVisible }, idx) in items"
        :ref="elRef"
        :class="{ visible: isVisible.value }"
      >
        item {{ idx }} {{ isVisible }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref, unref, onMounted } from "vue";
const list = ref(null);
const items = Array.from({ length: 42 }, () => ({
  elRef: ref(null),
  isVisible: ref(true)
}));

onMounted(() => {
  const refsMap = new WeakMap();

  const onObserve = (entries, observer) => {
    for (const entrie of entries) {
      const { isIntersecting, target } = entrie;

      const { isVisible } = refsMap.get(target);
      isVisible.value = isIntersecting;
    }
  };

  const observer = new IntersectionObserver(onObserve, {
    root: list.value,
    threshold: 1.0
  });

  items.forEach((it) => {
    refsMap.set(it.elRef.value, it);
    observer.observe(it.elRef.value);
  });
});
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  color: #2c3e50;
}

ul {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
  overflow: auto;
}

li {
  flex: 0 0;
  padding: 15px;
  margin: 5px;
  background-color: #ccc;
  text-align: center;
  transition: background-color 300ms;
}

li.visible {
  background-color: forestgreen;
}
</style>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.