如何用原生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'; });
核心逻辑解释
动态位置计算:
用getBoundingClientRect()获取元素相对于视口的坐标,再转换为相对于main容器的位置,完美解决main容器高度随设备变化的问题,不需要固定公式。双向触发判断:
- 向下滚动:当标题顶部到达main容器顶部(或超过),且标题底部还在容器内部时,触发对应info显示。
- 向上滚动:当标题完全滚出main容器底部时,自动切换到前一个标题的info。
性能优化:
- 用
currentActiveInfo记录当前激活的info,避免滚动时重复调用chngInfo。 - 加入节流函数,限制滚动事件的触发频率,避免频繁计算导致的性能问题。
- 用
边界处理:
- 页面加载时自动初始化第一个info,避免初始状态无内容。
- 向上滚动到第一个标题时,不会出现无info的情况。
注意事项
- 确保main容器的
overflow-y设置为auto或scroll,否则不会产生滚动条,监听事件无效。 - 如果标题元素有嵌套,
getBoundingClientRect()依然能准确计算位置,不受嵌套结构影响。
内容的提问来源于stack exchange,提问作者MrCycling
相关产品推荐
相关产品推荐

