使用IntersectionObserver监听消息列表

591 阅读1分钟

需求背景:给滚动到视野中的消息设置已读状态

实现方案:1.IntersectionObserver;2.监听scroll事件

1. 使用IntersectionObserver实现

  • 实例化IntersectionObserver,添加观察者
  const observer = new IntersectionObserver(entries => {
    ...
  });
  // 获取需要观察的每条消息的dom元素
  const messageItemComps = this.$refs.messageItem;
  messageItemComps.forEach(item => {
    // 添加观察者
    observer.observe(item.$el);
  });
  • 设置已读状态、移除观察者
const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      // 当前消息的dom元素完全出现时设置消息为已读
      if (entry.intersectionRatio === 1) {
        const index = entry.target.dataset.index;
        this.setMsgRead(this.messagesList[index]);
        // 设置完成之后移除观察者
        observer.unobserve(entry.target);
      }
    });
});
...
  • 存在问题
    • 多条消息未读的情况下,快速滚动会造成接口请求并发,等待时间长,性能不好

2. 消息批量设置已读(监听scroll事件)

**监听滚动容器的scroll事件,计算scrollTop + clientHeight区域可以容纳的消息数,批量设置已读**,参考如下:
 // 获取滚动容易,监听滚动事件
 const messageWrap = this.$refs.listWrap;
 messageWrap.addEventListener('scroll', this.handleScroll);

滚动事件参考:

// 避免频繁触发可为scroll事件添加防抖
handleScroll: debounce(function() {
    // 向上滚动高度
    const scrollTop = this.$refs.listWrap.scrollTop;
    // 屏幕高度
    const clientHeight = document.body.clientHeight;
    // 消息列表初始高度值
    let height = 0;
    // 滚动到的消息index
    let index = 0;
    const messageItemComps = this.$refs.messageItem;
    for (let i = 0; i < messageItemComps.length; i++) {
      const item = messageItemComps[i];
      height = height + item.$el.offsetHeight;
      if (height > (scrollTop + clientHeight)) {
        index = i;
        break;
      }
    }
    // 消息列表高度在一屏内
    if (height <= scrollTop + clientHeight) {
      index = this.messagesList.length;
    }
    // 获取未读消息
    const setReadMsgsList = this.messagesList.slice(0, index).filter(item => !item.isRead);
    // 批量设置已读
    this.setMsgReadBatch(setReadMsgsList);
}, 300),

注意:滚动事件滚动才会触发,首次打开视野内消息设置已读需要主动调用一次

最后要记得移除监听事件:

beforeDestroy() {
  const messageWrap = this.$refs.listWrap;
  messageWrap.removeEventListener('scroll', this.handleScroll);
}