使用 IntersectionObserver 在元素完全通过阈值后触发事件

Use IntersectionObserver To Trigger Event AFTER Element Completely Passes Threshold

我有一些 IntersectionObserver 的设置。 observer 切换要在用户向下滚动页面时制作的新框。 lastBoxObserver 在持续滚动时加载新框。

我想做的是在框离开第一个观察者中设置的阈值(observer - 其阈值设置为 1)后更改框的颜色。因此,一旦方框 12 进入视口,它就会通过观察者,一旦它完全超出该观察者的阈值并且方框 13 进入观察者,方框 12 的背景可能会从绿色变为橙色。

有没有办法做到这一点?也许通过添加额外的观察者,或向 observer 添加代码?非常感谢任何帮助。

代码笔:https://codepen.io/jon424/pen/NWwReEJ

JavaScript

 const boxes = document.querySelectorAll(".box");
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          entry.target.classList.toggle("show", entry.isIntersecting);
          if (entry.isIntersecting) observer.unobserve(entry.target);
        });
      },
      {
        threshold: 1,
      }
    );

    const lastBoxObserver = new IntersectionObserver((entries) => {
      const lastBox = entries[0];
      if (!lastBox.isIntersecting) return;
      loadNewBoxes();
      lastBoxObserver.unobserve(lastBox.target);
      lastBoxObserver.observe(document.querySelector(".box:last-child"));
    }, {});

    lastBoxObserver.observe(document.querySelector(".box:last-child"));

    boxes.forEach((box) => {
      observer.observe(box);
    });

    const boxContainer = document.querySelector(".container");

    function loadNewBoxes() {
      for (let i = 0; i < 1000; i++) {
        const box = document.createElement("div");
        box.textContent = `${i + 1}`;
        box.classList.add("box");
        observer.observe(box);
        boxContainer.appendChild(box);
      }
    }

HTML

   <div class="container">
      <div class="box">0</div>
    </div>

CSS

 .container {
      display: flex;
      flex-direction: column;
      gap: 1rem;
      align-items: flex-start;
    }

    .box {
      background: green;
      color: white;
      font-size: 4rem;
      text-align: center;
      margin: auto;
      height: 100px;
      width: 100px;
      border: 1px solid black;
      border-radius: 0.25rem;
      padding: 0.5rem;
      transform: translateX(100px);
      opacity: 0;
      transition: 150ms;
    }

    .box.show {
      transform: translateX(0);
      opacity: 1;
    }

    .box.show.more {
      background-color: orange;
    }

你只需要在你的第一个观察者中添加颜色变化逻辑。

我对您的代码进行了以下更改进行了测试,

在 css 中,将 .box.show.more 更改为 -

.box.more {
      background-color: orange;
}

在javascript-

const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          entry.target.classList.toggle("show", entry.isIntersecting);
          if (entry.isIntersecting) {
            observer.unobserve(entry.target);
            
            if(entry.target.textContent === '13')
                      entry.target.previousSibling.classList.toggle('more');
          }
        });
      },
      {
        threshold: 1,
      }
    );

如您所见,唯一的变化是我添加了这部分-

if(entry.target.textContent === '13')
    entry.target.previousSibling.classList.toggle('more');

为了便于测试,我还更改了新 div 的数量,从 1000 增加到 20。

如果您想在框 13 进入视口后立即更改框 12 的颜色,只需将“阈值”从 1 更改为 0。

<!DOCTYPE html>
<html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title></title>

    </head>
    
    <body>
        <style>
            .container {
                  display: flex;
                  flex-direction: column;
                  gap: 1rem;
                  align-items: flex-start;
                }
            
                .box {
                  background: green;
                  color: white;
                  font-size: 4rem;
                  text-align: center;
                  margin: auto;
                  height: 100px;
                  width: 100px;
                  border: 1px solid black;
                  border-radius: 0.25rem;
                  padding: 0.5rem;
                  opacity: 0;

                }
            
                .box.show {
                  opacity: 1;
                }
            
                .box.show.more {
                  background-color: orange;
                }
                
                .mytest{
                    border:solid 10px #000;
                    background-color: orange;
                }
        </style>
        <div class="container">
            <div class="box">0</div>
            <div class="sentinel"></div>
        </div>

  
  <script>
   /*  */   
   const observer = new IntersectionObserver(

     (entries) => {
       entries.forEach((entry) => {
         
         if (entry.isIntersecting) {
            entry.target.classList.add("show");  
        }
         if (entry.boundingClientRect.top < entry.rootBounds.top) {
           /* entry is above viewport */
           entry.target.classList.add("mytest");
           observer.unobserve(entry.target);
         }  
         
              
       });
          
     },
     {
       threshold: 0, /* top is 200 below top of viewport - something for the box to scroll into*/
       rootMargin: "-200px 0px 0px 0px"
     }
   );
   
   /* last div sentinel indicates last box */
   const mysentinel = new IntersectionObserver((entries) => {
     var entry = entries[0];
     if (entry.isIntersecting) {
       loadNewBoxes();
     }
   },{
        threshold: 0
   });
   

      const boxContainer = document.querySelector(".container");
      const sentinel = document.querySelector(".sentinel");
      /* setup - create extra boxes, sentinel is last after boxes */
      
      loadNewBoxes();
      
      mysentinel.observe(sentinel);
   
   function loadNewBoxes() {

     for (let i = 0; i < 10; i++) {
       const box = document.createElement("div");
       box.textContent = `${i + 1}`;
       box.classList.add("box");
       box.classList.add("show");
       observer.observe(box);
       boxContainer.appendChild(box);
     }
   
     boxContainer.appendChild(sentinel);

   }
   
        </script>
    </body>

</html>

IntersectionObserver 回调是异步的,如果你滚动得足够快,那么一些框会被遗漏。我已经简化了你的代码。如果 box/observed 目标在根顶部之上,那么我添加一个 class 并取消观察它。如果框正在转换,我添加一个 class 来显示框。我使用 sentinel 来指示最后一个框,并在 sentinel 到达视口时添加更多框。