微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在滚动时替换粘性元素? 上下文问题代码

如何解决如何在滚动时替换粘性元素? 上下文问题代码

上下文

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

在编码时,我尝试使用 HTML 实现效果,如果它不起作用,则使用 CSS,如果仍然不起作用,则使用 JS。我相信这是一种很好的做法,因为它使用内置功能并减少所需的计算资源,但如果您不同意此观点,请告诉我。

问题

理想情况下,元素的样式会在“卡住”时发生变化。为此,我查看了使用 IntersectionObserverDavid Walsh's solution,但在添加多个元素时会出现故障。

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

代码

这是一个片段。我也做了一个jsfiddle with the same code

//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>

解决方法

首先,您只需要一个 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",{threshold: [0,1]});

document.querySelectorAll(".myElement").forEach(i => observer.observe(i));

现在,从可见到粘性(或反之亦然)的元素和从不可见到粘性(反之亦然)的元素都切换了它们的类。

,

您在 JS 的最后一行中犯了一个错误。改为:

document.querySelectorAll(".myElement").forEach((i) => {
  const observer = new IntersectionObserver(
  ([i]) => i.target.classList.toggle("is-pinned",i.intersectionRatio < 1),{
    threshold: [1]
  });
  observer.observe(document.querySelector(".myElement")); // Use the element instead!
})

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。