交集观察者:根元素的填充更改 'boundaries'?

Intersection Observer: padding changes 'boundaries' of root element?

当我 运行 Chrome (98)(或 Safari 15.3)中的示例(全页视图)时,第一个元素在滚动一点点时直接消失,所以有点 'when leaving the non-padding or entering the padding-area'
这些选项定义了一个 rootMargin: '0px 0px 0px 0px' ,我认为它指的是根元素边框。这就是它在 Firefox(97) 中的行为方式。内部元素仅在到达顶部时隐藏。

有没有办法将包装器边框定义为边界而不是 'inner padding-border'?

        const wrapper = document.querySelector('#wrapper')
        const firstElem = document.querySelector('#first-elem')
        const options = {
            root: wrapper,
            rootMargin: '0px 0px 0px 0px',
            threshold: 1
        }
        const observer = new IntersectionObserver(handleFade, options);
        observer.observe(firstElem)

        function handleFade(entries) {
            entries.forEach(entry => {
                let target = entry.target
                if (entry.isIntersecting) {
                    target.classList.remove('fade-out')
                } else {
                    target.classList.add('fade-out')
                }
            })
        }
body {
        padding: 0;
        overflow: hidden;
    }
    #wrapper {
        height: 100vh;
        padding-top: 10rem;
        overflow: auto;
        border: 1px solid darkmagenta;
        box-sizing: border-box;
    }
    .elem {
        border: 3px solid teal;
        padding: 0 2rem;
        height: 20rem;
    }
.fade-out {
        visibility: hidden;
    }
<div id="wrapper">
    <div class="elem" id="first-elem">first watched element</div>
    <div class="elem">element</div>
    <div class="elem">element</div>
    <div class="elem">element</div> 
</div>

没错。这可能有点令人困惑,但根据 specs.

这似乎是预期的行为

当您为 IntersectionObserver 实例设置 root 时,根元素的 so-called intersection rectangle — the area that the intersection is checked against — will be the content area

内容区域是指元素最里面的区域,不包括所有的paddings:

至于这个问题的解决方案,您可以简单地使用一个额外的包装器元素来设置填充,然后从 root 元素中删除所有填充:

const wrapper = document.querySelector('#wrapper');
const firstElem = document.querySelector('#first-elem');

const observer = new IntersectionObserver(entries => {
    entries[0].target.classList.toggle('fade-out', !entries[0].isIntersecting)
}, {
    root: wrapper,
    threshold: 1,
});
  
observer.observe(firstElem)
body {
    padding: 0;
    margin: 0;
    overflow: hidden;
}

#wrapper {
    height: 100vh;
    overflow: auto;
    box-sizing: border-box;
}

#content {
    border: 1px solid darkmagenta;
    padding: 20px;
    padding-top: 10rem;
}

.elem {
    background-color: #eee;
    border: 2px solid blue;
    padding: 20px 30px;
    height: 100px;
    margin-bottom: 20px;
}

.fade-out {
    visibility: hidden;
}
<div id="wrapper">
  <div id="content">
    <div class="elem" id="first-elem">first watched element</div>
    <div class="elem">element</div>
    <div class="elem">element</div>
    <div class="elem">element</div> 
    <div class="elem">element</div> 
    <div class="elem">element</div> 
    <div class="elem">element</div> 
    <div class="elem">element</div> 
  </div>
</div>

虽然你的例子有点奇怪,但我不确定你想做什么,但无论如何,任何时候你想抵消填充对 root 的影响但是您的内容周围仍然有填充,解决方案是在额外的包装元素上声明填充。

鉴于滚动的 wrapper 元素占据了页面的整个高度,一个不同的解决方案是简单地从选项中删除 root: wrapper,这样元素的位置就会相对于视口

const wrapper = document.querySelector('#wrapper')
        const firstElem = document.querySelector('#first-elem')
        const options = {
            rootMargin: '0px 0px 0px 0px',
            threshold: 1
        }
        const observer = new IntersectionObserver(handleFade, options);
        observer.observe(firstElem)

        function handleFade(entries) {
            entries.forEach(entry => {
                let target = entry.target
                if (entry.isIntersecting) {
                    target.classList.remove('fade-out')
                } else {
                    target.classList.add('fade-out')
                }
            })
        }
body {
        padding: 0;
        overflow: hidden;
    }
    #wrapper {
        height: 100vh;
        padding-top: 10rem;
        overflow: auto;
        border: 1px solid darkmagenta;
        box-sizing: border-box;
    }
    .elem {
        border: 3px solid teal;
        padding: 0 2rem;
        height: 20rem;
    }
.fade-out {
        visibility: hidden;
    }
<div id="wrapper">
    <div class="elem" id="first-elem">first watched element</div>
    <div class="elem">element</div>
    <div class="elem">element</div>
    <div class="elem">element</div> 
</div>