You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React如何监听滚动获取视口内#parent的可见子div列表

实现方案

优先用浏览器原生IntersectionObserver实现,比手动监听scroll事件计算位置性能好很多,不会频繁触发重排阻塞主线程。
首先先修正你代码里的笔误:id为parent的容器闭合标签写错了,基础结构参考:

<div id="parent">
  <!-- 遍历渲染的列表项 -->
  <div id={item.id}>{item.title}</div>
</div>

方案1:IntersectionObserver 实现(推荐)

这个API会自动监听元素和视口/指定滚动容器的交叉状态,不需要绑定高频触发的滚动事件。

// 存当前可见的元素,用Set方便增删去重
const visibleItems = new Set()
const parentEl = document.getElementById('parent')

// 初始化观察器
const itemObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // 元素进入可见范围,加入集合
      visibleItems.add(entry.target)
    } else {
      // 元素离开可见范围,移出集合
      visibleItems.delete(entry.target)
    }
  })
  // 这里随时能拿到当前所有可见的子div列表
  console.log('可见元素列表:', Array.from(visibleItems))
}, {
  // 根节点默认是浏览器视口,如果是检测parent容器内部滚动的可见性,改成 root: parentEl
  root: null,
  // 可见阈值:0代表元素刚露1px就算可见,1代表完全露出才算可见,支持传数组配置多触发点
  threshold: 0
})

// 把parent下所有子div注册到观察器
// 如果列表是动态渲染的,每次新增列表项后要给新元素单独调用observe方法
Array.from(parentEl.querySelectorAll('div')).forEach(itemEl => {
  itemObserver.observe(itemEl)
})

// 不需要监听时(比如页面/组件卸载)调用销毁方法
// itemObserver.disconnect()

方案2:scroll事件监听实现(兼容老旧浏览器)

如果要兼容不支持IntersectionObserver的老浏览器,可以用这个方案,一定要加防抖,否则滚动时计算太频繁会卡顿。

// 防抖工具函数
function debounce(fn, delay = 100) {
  let timer = null
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

const parentEl = document.getElementById('parent')
// 计算可见元素的逻辑加防抖
const calcVisibleItems = debounce(() => {
  const allChildDivs = Array.from(parentEl.querySelectorAll('div'))
  const viewportHeight = window.innerHeight || document.documentElement.clientHeight
  const viewportWidth = window.innerWidth || document.documentElement.clientWidth

  const visibleList = allChildDivs.filter(el => {
    const rect = el.getBoundingClientRect()
    // 判断元素是否和视口有交叉
    return (
      rect.top < viewportHeight &&
      rect.bottom > 0 &&
      rect.left < viewportWidth &&
      rect.right > 0
    )
  })

  console.log('可见元素列表:', visibleList)
  return visibleList
})

// 绑定滚动、窗口大小变化事件
window.addEventListener('scroll', calcVisibleItems)
window.addEventListener('resize', calcVisibleItems)
// 页面初始化时先执行一次拿初始可见元素
calcVisibleItems()

// 不需要监听时记得移除事件
// window.removeEventListener('scroll', calcVisibleItems)
// window.removeEventListener('resize', calcVisibleItems)

注意事项

  • 如果列表是动态增删的,两个方案都要做对应处理:IntersectionObserver方案要给新增元素注册监听、给移除元素取消监听;scroll方案不需要额外处理,每次计算都会拿全量子元素判断。
  • 可见判断规则可以按业务调整:比如要求元素露出一半才算可见,就修改判断条件,或者把IntersectionObserver的threshold改成0.5。
  • 如果是在局部滚动容器(不是整页滚动)里检测,IntersectionObserver要把root设为对应的滚动容器,scroll方案要把视口宽高的计算换成滚动容器的宽高和滚动位置。

内容的提问来源于stack exchange,提问作者Chandler Bing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:03:19