窗口resize时使用getBoundingClientRect()校正导航指示器位置是否合理?
现有方案的问题
你当前的实现功能完全可用,唯一的潜在问题是resize事件触发频率极高,拖拽窗口调整大小时每秒会触发数十次,每次调用getBoundingClientRect()都会强制浏览器重新计算元素布局触发重排,若后续页面复杂度提升、存在大量DOM元素时,可能会出现明显的性能损耗,而且用户快速调整窗口大小的过程中,指示器也会出现频繁跳变的观感问题。
另外你现有代码里每次连续调用两次getBoundingClientRect(),可以把返回值存在变量里复用,减少一次重排触发。
优化方案
方案1:给resize事件加防抖(最小改动适配现有逻辑,保留指示器滑动效果)
对resize的回调函数做防抖处理,设置200ms左右的延迟,确保只有窗口调整结束后才执行一次计算,大幅降低触发频率:
// 防抖工具函数 function debounce(func, delay = 200) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); } } const navigator = document.querySelector('.navigator'); const firstOption = document.querySelector('.first-option'); const navOptions = document.querySelectorAll('.nav-option'); const nav = document.querySelector('nav'); // 复用getBoundingClientRect返回值 const firstRect = firstOption.getBoundingClientRect(); navigator.style.left = `${firstRect.left}px`; navigator.style.width = `${firstRect.width}px`; nav.addEventListener('click', function(e) { if(e.target.classList.contains('nav-option')) { navOptions.forEach(option => option.classList.remove('nav-option-active')); e.target.classList.add('nav-option-active'); const activeRect = e.target.getBoundingClientRect(); navigator.style.left = `${activeRect.left}px`; navigator.style.width = `${activeRect.width}px`; }; }); // 绑定防抖后的resize回调 window.addEventListener('resize', debounce(function() { let navOptionActive = nav.querySelector('.nav-option-active'); const activeRect = navOptionActive.getBoundingClientRect(); navigator.style.left = `${activeRect.left}px`; navigator.style.width = `${activeRect.width}px`; }));
方案2:纯CSS实现指示器(性能最优,完全不需要处理resize逻辑)
如果不需要指示器跨选项滑动的动效,可以直接把指示器做成导航选项的伪元素,不需要单独的.navigatorDOM节点,也完全不用监听resize事件,指示器会自动跟随激活选项的位置变化:
调整后的CSS
* { margin: 0; padding: 0; } nav { position: relative; display: flex; justify-content: space-between; align-items: center; margin: 100px auto; padding: 7vh 30vw; width: auto; background:#eeeeee; } .nav-option { padding: 0 15px; font-size: 22px; cursor: pointer; position: relative; /* 新增相对定位 */ } /* 新增伪元素作为指示器 */ .nav-option::after { content: ''; position: absolute; left: 0; bottom: -7vh; /* 适配nav的底部padding,调整到导航栏底部即可 */ width: 0; height: 5px; background: orangered; transition: .4s ease all; } .nav-option.nav-option-active::after { width: 100%; } @media (max-width: 1200px) { .nav-option { font-size: 18px; padding: 10px; } }
调整后的JS
删掉所有和.navigator相关的操作、以及resize监听即可:
const navOptions = document.querySelectorAll('.nav-option'); const nav = document.querySelector('nav'); nav.addEventListener('click', function(e) { if(e.target.classList.contains('nav-option')) { navOptions.forEach(option => option.classList.remove('nav-option-active')); e.target.classList.add('nav-option-active'); }; });
内容的提问来源于stack exchange,提问作者xander17
相关产品推荐
相关产品推荐

