DEV Community

Emily Scott
Emily Scott

Posted on

Why JavaScript IntersectionObserver Is Not Triggering (And How to Fix It)

A less common but very frustrating frontend problem developers face is this:

You set up IntersectionObserver, but it never triggers.

No callback.

No lazy loading.

No infinite scroll.

Nothing happens.

This issue appears a lot in:

  • Infinite scrolling
  • Lazy loading images
  • Scroll animations
  • Ad tracking
  • Analytics events
  • Sticky navigation triggers
  • Performance optimization

It feels like your code is correct, but the observer still does nothing.

Let’s fix it step by step.


The Problem

Suppose you want to detect when an element enters the screen.

You write this:

const target = document.querySelector(".box");

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      console.log("Element is visible");
    }
  });
});

observer.observe(target);
Enter fullscreen mode Exit fullscreen mode

You expect this:

Element is visible
Enter fullscreen mode Exit fullscreen mode

when the element enters the viewport.

But instead, nothing happens.

No logs.

No trigger.

Very confusing.


Why This Happens

IntersectionObserver only works when:

  1. The target element actually exists
  2. The element can be observed inside the viewport
  3. The observer settings are correct
  4. The CSS layout allows proper visibility detection

If even one of these breaks, the callback may never run.

That is usually the real issue.


Common Mistake #1: The Element Does Not Exist Yet

This is very common:

const target = document.querySelector(".box");
observer.observe(target);
Enter fullscreen mode Exit fullscreen mode

If .box is not rendered yet:

target === null
Enter fullscreen mode Exit fullscreen mode

then observation fails silently.

This is especially common in React and dynamic UI rendering.


Step 1: Check the Target First

Safer version:

const target = document.querySelector(".box");

if (target) {
  observer.observe(target);
}
Enter fullscreen mode Exit fullscreen mode

Always confirm the element exists first.

This solves many issues immediately.


Common Mistake #2: Wrong threshold Value

Developers often use this:

const observer = new IntersectionObserver(callback, {
  threshold: 1
});
Enter fullscreen mode Exit fullscreen mode

Problem:

threshold: 1 means:

Trigger only when 100% of the element is visible

That often never happens.

Especially with large elements.

Very common mistake.


Step 2: Use a Better Threshold

Safer version:

const observer = new IntersectionObserver(callback, {
  threshold: 0.1
});
Enter fullscreen mode Exit fullscreen mode

This means:

Trigger when 10% of the element becomes visible

Much more practical.

Much better for real applications.


Common Mistake #3: Wrong root Setting

Some developers set this:

root: document.querySelector(".container")
Enter fullscreen mode Exit fullscreen mode

But if that container does not actually scroll, the observer behaves strangely.

If you want viewport detection, use:

root: null
Enter fullscreen mode Exit fullscreen mode

which means the browser viewport.

This is usually the correct choice.


Step 3: Use the Viewport Properly

Correct setup:

const observer = new IntersectionObserver(callback, {
  root: null,
  threshold: 0.1
});
Enter fullscreen mode Exit fullscreen mode

Clean.

Reliable.

Production-safe.


React Version of This Problem

This happens constantly in React.

Example:

useEffect(() => {
  observer.observe(ref.current);
}, []);
Enter fullscreen mode Exit fullscreen mode

Problem:

Sometimes ref.current is still null.

Then the observer never starts.

Very common bug.


Step 4: Protect React Refs

Safer version:

useEffect(() => {
  if (ref.current) {
    observer.observe(ref.current);
  }
}, []);
Enter fullscreen mode Exit fullscreen mode

Always protect refs before observing.

This saves a lot of debugging time.


Another Hidden Problem: CSS Layout Issues

Sometimes the problem is not JavaScript.

It is CSS.

Examples include:

  • display: none
  • overflow: hidden
  • Wrong scroll container
  • Zero-height elements
  • Elements already visible on load

These directly affect visibility detection.

Developers often miss this completely.

Very important.


Step 5: Debug with entry

Always inspect the observer data:

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    console.log(entry);
  });
});
Enter fullscreen mode Exit fullscreen mode

This helps you check:

  • Is the callback firing?
  • Is isIntersecting false?
  • Is the intersection ratio too small?

This reveals the real issue fast.


Quick Debug Rule

Whenever IntersectionObserver fails, ask yourself:

Is the observer broken, or is the element never actually intersecting?

That question usually finds the issue immediately.

Most bugs start there.


Final Thoughts

IntersectionObserver is powerful, but small setup mistakes break everything.

Remember:

  • Check that the target element exists
  • Avoid threshold: 1 unless truly needed
  • Use root: null for viewport detection
  • Protect React refs
  • Check CSS layout issues
  • Debug using the full entry object

This is a less common problem, but when it happens, debugging becomes painful.

Fixing it properly saves serious time.

Especially in infinite scroll and lazy loading features.


Your Turn

Have you ever spent an hour debugging IntersectionObserver only to realize the element was null?

That happens more often than developers admit.

Top comments (0)