【问题标题】:How do I use a single IntersectionObserver to perform unique callbacks per observed element?如何使用单个 IntersectionObserver 为每个观察到的元素执行唯一的回调?
【发布时间】:2021-09-18 00:48:22
【问题描述】:

当向下滚动页面时,我需要知道数十个 HTMLElement 何时位于视口内部或外部。所以我使用IntersectionObserver API 来创建VisibilityHelper 类的多个实例,每个实例都有自己的IntersectionObserver。使用这个辅助类,我可以检测任何 HTMLElement 何时 50% 可见或隐藏:

工作演示:

// Create helper class
class VisibilityHelper {
  constructor(htmlElem, hiddenCallback, visibleCallback) {
    this.observer = new IntersectionObserver((entities) => {
      const ratio = entities[0].intersectionRatio;
      if (ratio <= 0.0) {
        hiddenCallback();
      } else if (ratio >= 0.5) {
        visibleCallback();
      }
    }, {threshold: [0.0, 0.5]});

    this.observer.observe(htmlElem);
  }
}

// Get elements
const headerElem = document.getElementById("header");
const footerElem = document.getElementById("footer");

// Use helper class to know whether visible or hidden
const headerViz = new VisibilityHelper(
  headerElem,
  () => {console.log('header is hidden')},
  () => {console.log('header is visible')},
);
const footerViz = new VisibilityHelper(
  footerElem,
  () => {console.log('footer is hidden')},
  () => {console.log('footer is visible')},
);
#page {
  width: 100%;
  height: 1500px;
  position: relative;
  background: linear-gradient(#000, #fff);
}
#header {
  position: absolute;
  top: 0;
  width: 100%;
  height: 100px;
  background: #f90;
  text-align: center;
}
#footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 100px;
  background: #09f;
  text-align: center;
}
<div id="page">
  <div id="header">
  Header
  </div>


  <div id="footer">
  Footer
  </div>
</div>

问题是我上面的演示为每个需要观看的 HTMLElement 创建了一个IntersectionObserver。我需要在 100 个元素上使用它,this question 表示出于性能原因,我们应该每页只使用 一个 IntersectionObserver。其次,the API also suggests 一个观察者可以用来观察多个元素,因为回调会给你一个条目列表。

如何使用单个 IntersectionObserver 监视多个 htmlElements 并为每个元素触发唯一的隐藏/可见回调?

【问题讨论】:

  • 这很难,因为正如您所提到的,您对正在观察的不同元素有独特的回调。我在 SO 上发现了这可能会有所帮助 - 本质上是使用传递的条目数据属性来更改回调:stackoverflow.com/questions/52460010/…

标签: javascript html oop intersection-observer


【解决方案1】:

您可以在目标元素及其可见性状态之间定义回调映射。然后在您的IntersectionObserver 回调中,使用IntersectionObserverEntry.target 读取id 并根据visiblehidden 的可见性状态从地图中调用关联的回调。

这是基于您的示例的简化方法。该方法的要点是定义callbacks 映射并从IntersectionObserverEntry 读取target

// Create helper class
class VisibilityHelper {
  constructor(htmlElems, callbacks) {
this.callbacks = callbacks;
this.observer = new IntersectionObserver(
  (entities) => {
    const ratio = entities[0].intersectionRatio;
    const target = entities[0].target;

    if (ratio <= 0.0) {
      this.callbacks[target.id].hidden();
    } else if (ratio >= 0.5) {
      this.callbacks[target.id].visible();
    }
  },
  { threshold: [0.0, 0.5] }
);

htmlElems.forEach((elem) => this.observer.observe(elem));
  }
}

// Get elements
const headerElem = document.getElementById("header");
const footerElem = document.getElementById("footer");

// Use helper class to know whether visible or hidden
const helper = new VisibilityHelper([headerElem, footerElem], {
  header: {
visible: () => console.log("header is visible"),
hidden: () => console.log("header is hidden"),
  },
  footer: {
visible: () => console.log("footer is visible"),
hidden: () => console.log("footer is hidden"),
  },
});
#page {
  width: 100%;
  height: 1500px;
  position: relative;
  background: linear-gradient(#000, #fff);
}
#header {
  position: absolute;
  top: 0;
  width: 100%;
  height: 100px;
  background: #f90;
  text-align: center;
}
#footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 100px;
  background: #09f;
  text-align: center;
}
<div id="page">
  <div id="header">
  Header
  </div>


  <div id="footer">
  Footer
  </div>
</div>

如果您想遍历entities 的整个数组而不是只考虑第一个entities[0],则可以使用类似的方法。

【讨论】:

  • 感谢您的回答。我看到你想做什么,使用 ID。我想这是我在示例中使用 ID 的错,但我不一定每个元素都有唯一的 ID。我想我可以 create custom data attributes 代替,但感觉有点 hacky。我希望IntersectionObserver 有一个我不知道的更面向对象的方法。
  • 您需要某种方式来引用将被观察到的 HTML 元素,无论是 idclass 还是 data- 属性。根据您的要求,可以在 IntersectionObserver 回调中定义与每个 HTML 元素关联的回调,如果这就是 more object-oriented approach 的意思。
猜你喜欢
  • 1970-01-01
  • 2022-07-29
  • 1970-01-01
  • 2019-10-26
  • 1970-01-01
  • 1970-01-01
  • 2021-02-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多