为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

翻翻过去那场雪 2024-01-18 17:02:55
语境我正在制作一个博客网站,我希望有一个粘性元素,该元素会随着用户滚动而在每个新的一年和月份进行更新。这样标题就会显示列出的博客文章的当前月份和年份。编码的时候,我尝试用HTML实现效果,如果不行就用CSS,如果还是不行就用JS。我相信这是一个很好的做法,因为它使用内置功能并减少了所需的计算资源,但如果您不同意这个观点,请告诉我。问题理想情况下,元素的样式在“卡住”时会发生变化。为此,我查看了David Walsh 的解决方案,该解决方案使用IntersectionObserver但在添加多个元素时会出现故障。我面临的主要问题是,当有多个条目时,脚本会将位于窗口底部边框的元素检测为“固定”。代码这是一个片段。我还用相同的代码制作了一个jsfiddle 。//Essentially putting David Walsh's code in a loopdocument.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>
查看完整描述

2 回答

?
开心每一天1111

TA贡献1836条经验 获得超13个赞

首先,你只需要一个 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));

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


查看完整回答
反对 回复 2024-01-18
?
MMMHUHU

TA贡献1834条经验 获得超8个赞

你的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!

})


查看完整回答
反对 回复 2024-01-18
  • 2 回答
  • 0 关注
  • 33 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信