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

如何基于参数计算滚动文本显示窗口内的可见文本范围

功能实现方案

这个需求可以通过浏览器原生API实现,不需要依赖第三方库,核心逻辑和可复用代码如下:

1. 可视文本范围判断

不需要手动通过字体大小、行高参数计算,直接用Range和getBoundingClientRect()接口实现精准判断,误差可以控制在1像素以内:

  • 先调用scrollContainer.getBoundingClientRect()获取滚动容器的可视区域边界坐标
  • 用document.createTreeWalker遍历容器内的所有文本节点,为每个文本节点创建Range对象,调用range.getBoundingClientRect()拿到节点的位置坐标
  • 判断节点坐标是否和容器可视范围有重叠,重叠占比超过设定阈值(通常设为30%即可判定为用户可见)的部分即为当前展示的文本范围
  • 如果需要行级粒度的统计结果,可以调用Range.getClientRects()拿到每一行文本的单独坐标再做匹配

2. 事件监听与时长统计逻辑

  • 滚动事件做100~200ms的节流优化,避免高频触发影响页面性能,每次触发时更新当前可视文本范围
  • 监听resize事件,窗口尺寸变化时立即重新计算当前可视范围,同步记录对应时间戳
  • 维护两个数据集合:当前可视文本片段的首次展示时间戳映射表、所有文本片段的累计展示时长统计表
  • 每次范围更新时对比新旧可视集合:
    • 对于不再可视的片段,用当前时间戳减去首次展示时间,累计到该片段的总展示时长中,清除其首次展示时间记录
    • 对于新进入可视区的片段,记录当前时间戳作为首次展示时间
  • 快速滚动时如果片段可视时间小于300ms可以判定为无效浏览,不计入时长,避免误统计

核心参考代码

// 获取滚动容器内当前可视的文本片段
function getVisibleTextRanges(scrollContainer, visibleThreshold = 0.3) {
  const containerRect = scrollContainer.getBoundingClientRect();
  const visibleRanges = [];
  const textWalker = document.createTreeWalker(scrollContainer, NodeFilter.SHOW_TEXT, null);
  let currentTextNode;

  while (currentTextNode = textWalker.nextNode()) {
    if (!currentTextNode.textContent.trim()) continue; // 跳过空文本节点
    const nodeRange = document.createRange();
    nodeRange.selectNodeContents(currentTextNode);
    const nodeRect = nodeRange.getBoundingClientRect();

    // 计算重叠高度占比
    const overlapTop = Math.max(nodeRect.top, containerRect.top);
    const overlapBottom = Math.min(nodeRect.bottom, containerRect.bottom);
    const overlapHeight = Math.max(0, overlapBottom - overlapTop);
    const visibleRatio = overlapHeight / nodeRect.height;

    if (visibleRatio >= visibleThreshold) {
      visibleRanges.push({
        text: currentTextNode.textContent,
        visibleRatio: visibleRatio,
        nodeId: currentTextNode.parentNode.dataset.paragraphId || Math.random().toString(36).slice(2) // 可提前给段落绑定唯一ID方便统计
      });
    }
  }
  return visibleRanges;
}

// 节流工具函数
function throttle(fn, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      fn.apply(this, args);
    }
  }
}

// 统计逻辑绑定
const scrollContainer = document.querySelector('#your-scroll-container');
const visibleTextMap = new Map(); // 存储当前可视片段的首次展示时间
const durationStats = new Map(); // 存储各片段累计展示时长

const updateStats = throttle(() => {
  const currentVisible = getVisibleTextRanges(scrollContainer);
  const currentNodeIds = new Set(currentVisible.map(item => item.nodeId));
  const now = Date.now();

  // 处理不再可视的片段
  for (const [nodeId, startTime] of visibleTextMap.entries()) {
    if (!currentNodeIds.has(nodeId)) {
      const showDuration = now - startTime;
      if (showDuration >= 300) { // 过滤快速划过的无效时长
        durationStats.set(nodeId, (durationStats.get(nodeId) || 0) + showDuration);
      }
      visibleTextMap.delete(nodeId);
    }
  }

  // 处理新进入可视区的片段
  currentVisible.forEach(item => {
    if (!visibleTextMap.has(item.nodeId)) {
      visibleTextMap.set(item.nodeId, now);
    }
  });
}, 150);

scrollContainer.addEventListener('scroll', updateStats);
window.addEventListener('resize', updateStats);
// 页面隐藏/显示时也可同步触发统计修正
document.addEventListener('visibilitychange', updateStats);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 02:27:03