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

如何用原生JavaScript在元素抵达父Div顶部时触发显示切换事件?

原生JS实现内部滚动容器的标题触发逻辑

首先,我完全理解你的需求——不想为了一个简单效果引入jQuery,而且要适配动态高度的main容器,还要处理上下滚动的触发情况。下面是完整的实现方案,结合你的chngInfo函数来完成:

完整代码实现

HTML结构参考(假设你的结构类似这样)

<div class="info">
  <div id="info1">对应标题1的信息</div>
  <div id="info2" style="display:none;">对应标题2的信息</div>
  <div id="info3" style="display:none;">对应标题3的信息</div>
</div>
<div id="main" style="height:90vh; overflow-y:auto; position:relative;">
  <section>
    <h2 id="title1">标题1</h2>
    <p>这里是很长的内容,足够让main容器出现滚动条...</p>
  </section>
  <section>
    <h2 id="title2">标题2</h2>
    <p>这里是很长的内容...</p>
  </section>
  <section>
    <h2 id="title3">标题3</h2>
    <p>这里是很长的内容...</p>
  </section>
</div>

JavaScript逻辑

// 你已有的显示/隐藏函数
function chngInfo(x) { 
  const targets = document.querySelectorAll('[id^="info"]'); 
  for (let i = targets.length; i--;) { 
    targets[i].style.display = 'none'; 
  } 
  document.getElementById('info' + x).style.display = 'block'; 
}

// 保存当前激活的info编号,避免重复触发提升性能
let currentActiveInfo = null;

// 获取核心元素
const mainContainer = document.getElementById('main');
const sectionTitles = document.querySelectorAll('h2[id^="title"]');

// 节流函数:减少滚动事件触发频率,优化性能
function throttle(func, delay = 100) {
  let lastExecutionTime = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastExecutionTime >= delay) {
      func.apply(this, args);
      lastExecutionTime = now;
    }
  };
}

// 滚动事件处理核心逻辑
function handleMainScroll() {
  const mainRect = mainContainer.getBoundingClientRect();
  const mainHeight = mainContainer.clientHeight;

  sectionTitles.forEach(title => {
    const titleRect = title.getBoundingClientRect();
    // 计算标题相对于main容器顶部/底部的位置(视口坐标转容器内部坐标)
    const titleRelativeTop = titleRect.top - mainRect.top;
    const titleRelativeBottom = titleRect.bottom - mainRect.top;

    const infoNumber = title.id.replace('title', '');

    // 向下滚动:标题顶部抵达/超过main顶部,且未完全滚出容器
    const isAtMainTop = titleRelativeTop <= 0 && titleRelativeBottom > 0;
    // 向上滚动:标题完全滚出main底部
    const hasPassedMainBottom = titleRelativeBottom <= 0;

    if (isAtMainTop && currentActiveInfo !== infoNumber) {
      chngInfo(infoNumber);
      currentActiveInfo = infoNumber;
    } else if (hasPassedMainBottom && currentActiveInfo === infoNumber) {
      // 向上滚动时,激活前一个标题对应的info
      const titleIndex = Array.from(sectionTitles).indexOf(title);
      const prevTitle = sectionTitles[titleIndex - 1];
      if (prevTitle) {
        const prevInfoNum = prevTitle.id.replace('title', '');
        chngInfo(prevInfoNum);
        currentActiveInfo = prevInfoNum;
      }
    }
  });
}

// 添加滚动监听(用节流优化)
mainContainer.addEventListener('scroll', throttle(handleMainScroll));

// 页面初始化:默认显示第一个info
window.addEventListener('load', () => {
  chngInfo('1');
  currentActiveInfo = '1';
});

核心逻辑解释

  1. 动态位置计算:
    用getBoundingClientRect()获取元素相对于视口的坐标,再转换为相对于main容器的位置,完美解决main容器高度随设备变化的问题,不需要固定公式。

  2. 双向触发判断:

    • 向下滚动:当标题顶部到达main容器顶部(或超过),且标题底部还在容器内部时,触发对应info显示。
    • 向上滚动:当标题完全滚出main容器底部时,自动切换到前一个标题的info。
  3. 性能优化:

    • 用currentActiveInfo记录当前激活的info,避免滚动时重复调用chngInfo。
    • 加入节流函数,限制滚动事件的触发频率,避免频繁计算导致的性能问题。
  4. 边界处理:

    • 页面加载时自动初始化第一个info,避免初始状态无内容。
    • 向上滚动到第一个标题时,不会出现无info的情况。

注意事项

  • 确保main容器的overflow-y设置为auto或scroll,否则不会产生滚动条,监听事件无效。
  • 如果标题元素有嵌套,getBoundingClientRect()依然能准确计算位置,不受嵌套结构影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 10:52:28