如何在滚动时替换粘性元素?

语境

我正在制作一个博客网站,我希望有一个粘性元素,该元素会随着用户滚动而在每个新的一年和月份进行更新。这样标题就会显示列出的博客文章的当前月份和年份。

编码的时候,我尝试用HTML实现效果,如果不行就用CSS,如果还是不行就用JS。我相信这是一个很好的做法,因为它使用内置功能并减少了所需的计算资源,但如果您不同意这个观点,请告诉我。

问题

理想情况下,元素的样式在“卡住”时会发生变化。为此,我查看了David Walsh 的解决方案,该解决方案使用IntersectionObserver但在添加多个元素时会出现故障。

我面临的主要问题是,当有多个条目时,脚本会将位于窗口底部边框的元素检测为“固定”。

代码

这是一个片段。我还用相同的代码制作了一个jsfiddle 。

//Essentially putting David Walsh's code in a loop


document.querySelectorAll(".myElement").forEach((i) => {

const observer = new IntersectionObserver(([i]) => i.target.classList.toggle("is-pinned", i.intersectionRatio < 1),

{threshold: [1]});

observer.observe(i);

})

#parent { 

  height: 2000px; 

}


.myElement {

  position: sticky;

  top: -1px;

}


/* styles for when the header is in sticky mode. The transition times accentuate the undesired effect */

.myElement.is-pinned {

  color: red;

  transition: color 0.3s, background-color 0.3s;

  background-color: orange;

}

<div id="parent">

  <!-- Adding more than one 'hello' element. The br's are here to add vertical space and be longer than the viewport height -->

  <br><br><br><br>

  <div class="myElement">Hello!</div>

  

  <br><br><br><br>

  <div class="myElement">Hello 2!</div>

  

  <br><br><br><br>

  <div class="myElement">Hello 3!</div>

  

  <br><br><br><br>

  <div class="myElement">Hello 4!</div>

  

  <br><br><br><br>

  <div class="myElement">Hello 5!</div>

  

  <br><br><br><br>

  <div class="myElement">Hello 6!</div>

  

  <br><br><br><br>

  <div class="myElement">Hello 7!</div>

  

  <br><br><br><br>

  <div class="myElement">Hello 8!</div>

</div>


翻翻过去那场雪
浏览 86回答 2
2回答

开心每一天1111

首先,你只需要一个 IntersectionObserver。只要您需要相同的回调和选项(在本例中就是如此),您就可以observe()使用同一个观察者来处理多个元素。只有您observer.observe(i);需要在循环内。但是,如果您向上或向下跳转页面,则您的单个观察者可以同时调用多个条目。因此,您需要循环所有观察到的条目。更重要的是,intersectionRatio不关心元素在屏幕上的位置。元素在框的顶部和底部都跨越了 100% 可见性阈值。您只关心框顶部的元素。该IntersectionObserverEntry对象还有一个boundingClientRect属性可以告诉您该元素现在所在的位置。您可以使用它来仅切换顶部的元素。所以你最终会得到这样的结果:const observer = new IntersectionObserver((entries) => {    for (let i of entries) {        i.target.classList.toggle(            "is-pinned", i.boundingClientRect.y < 0);    }}, {threshold: [1]});document.querySelectorAll(".myElement").forEach(i => observer.observe(i));然而,这仍然给你带来了一个问题。在您的示例中,您滚动的框足够长,如果您直接从顶部跳到底部,则会出现从“框下方可见 0%”到“框顶部可见 99%”的元素。这不会超过 100% 阈值,因此 IntersectionObserver 回调永远不会为这些元素触发!这意味着他们没有上课is-pinned。您可以简单地向同一个观察者添加另一个 0% 的阈值来捕获这些变化:const observer = new IntersectionObserver((entries) => {    for (let i of entries) {        i.target.classList.toggle(            "is-pinned", i.boundingClientRect.y < 0);    }}, {threshold: [0, 1]});document.querySelectorAll(".myElement").forEach(i => observer.observe(i));现在,从可见到粘性(或反之亦然)的元素和从不可见到粘性(或反之亦然)的元素都会切换其类。

MMMHUHU

你的JS最后一行犯了一个错误。将其更改为:document.querySelectorAll(".myElement").forEach((i) => {&nbsp; const observer = new IntersectionObserver(&nbsp; ([i]) => i.target.classList.toggle("is-pinned", i.intersectionRatio < 1), {&nbsp; &nbsp; threshold: [1]&nbsp; });&nbsp; observer.observe(document.querySelector(".myElement")); // Use the element instead!})
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript