17

I am trying to invoke a callback via intersection observer.

I want the target to be style: "position: fixed" and move it via style.top.

I also specified the root element which is an ancestor of the target with style: "position: relative".

But when the target and the observer intersects, the callback function won't be triggered.

Are there some limitations I missed?

Here is what I typed:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>IO</title>
</head>
<body>
<div style="height: 200px;width: 100%;background: violet" class="upper">aaa</div>
<div style="position:relative;height: 200px;width: 100%;background: blueviolet" id="middle">bbb
    <div id="target" style="position:fixed;top: 0px;width: 50px;height: 50px;background: firebrick">ccc</div>
</div>
<script>
    let options = {
        root: document.getElementById("middle"),
        rootMargin: '0px',
        threshold: 0
    };
    let observer = new IntersectionObserver(entry => {
        console.log("observer's acting.")
    }, options);

    let target = document.getElementById("target");
    observer.observe(target);

    let stepping = 0;

    let cb = () => {
        target.style.top = stepping + 'px';
        stepping += 4;
        if (stepping < 300){
            setTimeout(cb, 100);
        }
    };

    window.addEventListener("click", () => {
        cb();
    })
</script>
</body>
</html>

And here is a codepen demo: codepen demo

You can click anywhere in the page to start moving the ccc block.

krave
  • 1,629
  • 4
  • 17
  • 36

2 Answers2

5

Elements with position: fixed are positioned relative to the viewport and the viewport moves. So, fixed positioned elements "move" as you scroll. Even though #target is a child of #middle, I believe the IntersectionObserver, with whatever it uses under the hood to calculate if the target is entering/leaving the root, never fires the callback because the target is outside of the document flow.

Here is a related issue. There isn't much out in the interwebs related to this issue: https://bugs.chromium.org/p/chromium/issues/detail?id=653240

Note: Setting position: absolute on the target does indeed fire the callback when entering and leaving the viewport.

snewcomer
  • 2,020
  • 1
  • 19
  • 22
  • 6
    In my own testing, it seems like `position: absolute` works as you would hope if the root is the viewport, but it has the same problem as `fixed` if you check for intersection with another element. I saw some language somewhere about this stating that the two observed elements must be in the same "block chain"; presumably that means neither is allowed to break out of rendering flow with `fixed` or `absolute`. That's a shame because collision between absolute and relative elements is a common thing to want to detect. I guess it's back to the old way for those cases. – Mattias Martens Jul 03 '20 at 22:48
  • Is this true? I am trying to detect visibility on slides that use positioning absolute with Intersection Observer. Is this a waste of time? – emb03 Sep 17 '20 at 13:10
  • @MattiasMartens it seems I hit the same roadblock.... position:absolute inside the overflow container is not detected, only whole viewport is detected. What would be second best way to deal with this, `getBoundingClientRect` ? – Perp Mar 09 '21 at 13:25
  • @Perp Yeah, if I remember correctly that's what I was referring to. In my case I was able to avoid the need for it but I suppose you would want to either monitor it regularly with setInterval() or attach listeners to window resize events, scroll events, and anything in the code that might change the dimensions or position of the element. – Mattias Martens Mar 09 '21 at 18:50
  • 2
    Just for completeness sake, I wanna confirm that `position: absolute` is working with Intersection Observer, I just had wrong implementation. That is most certainly not the problem, for anyone reading this. – Perp Mar 10 '21 at 10:39
  • @Perp what wasn't working the first time you tried it? And when you got it working with absolute, were you able to additionally confirm whether or not `fixed` also works? – Sam Oct 01 '21 at 19:17
-1

In my case I had my root element (with position:fixed) at the same DOM hierarchy level as the elements I wanted to observe (that were scrollable) and no events were triggered.

<div id="root" style="position:fixed"></div>
<div id="scrollable"></div>

When I placed the elements inside the root the events triggered.

<div id="root" style="position:fixed">
  <div id="scrollable"></div>
</div>
Jonathan
  • 1,007
  • 16
  • 12