如何阻止浏览器默认聚焦非编程设置焦点的元素时自动滚动
解决方案
有两种实践中验证可行的处理方式:
方案1:提前拦截焦点触发事件,手动控制聚焦逻辑
因为浏览器默认的聚焦行为触发前会先触发mousedown(点击场景)和keydown(Tab切换场景)事件,我们可以在这两个事件回调中主动触发带preventScroll参数的聚焦,同时阻止浏览器默认的聚焦行为,从根源避免非预期滚动:
const stickyCheckboxes = document.querySelectorAll('.sticky-col-checkbox'); const scrollContainer = document.querySelector('.table-scroll-container'); stickyCheckboxes.forEach(checkbox => { // 处理点击聚焦场景 checkbox.addEventListener('mousedown', (e) => { e.preventDefault(); checkbox.focus({ preventScroll: true }); // 保证点击后checkbox的选中状态正常切换 checkbox.checked = !checkbox.checked; }); // 处理Tab键切换聚焦场景 checkbox.addEventListener('keydown', (e) => { // 只捕获Tab触发的聚焦前事件 if (e.key === 'Tab') { const nextFocusEl = e.shiftKey ? checkbox.previousElementSibling : checkbox.nextElementSibling; // 如果下一个聚焦元素也是sticky列的checkbox,手动控制聚焦 if (nextFocusEl?.classList.contains('sticky-col-checkbox')) { e.preventDefault(); nextFocusEl.focus({ preventScroll: true }); } } }); });
方案2:监听focus事件,回滚非预期的水平滚动
如果不想拦截默认行为,也可以通过记录滚动容器的水平偏移量,在sticky元素触发focus时立刻重置滚动位置,该方案兼容性更好,且基本不会出现视觉抖动:
const scrollContainer = document.querySelector('.table-scroll-container'); let savedScrollLeft = 0; // 捕获阶段监听focus事件,更早拿到焦点触发信号 scrollContainer.addEventListener('focus', (e) => { if (e.target.classList.contains('sticky-col-checkbox')) { savedScrollLeft = scrollContainer.scrollLeft; // 下一个微任务立刻重置滚动位置,覆盖浏览器默认的滚动行为 requestAnimationFrame(() => { scrollContainer.scrollLeft = savedScrollLeft; }); } }, true);
可选CSS优化
部分浏览器下可以给sticky列元素添加scroll-snap-align: none,避免滚动吸附逻辑和焦点滚动逻辑冲突,进一步降低非预期滚动的概率:
.sticky-col { position: sticky; left: 0; scroll-snap-align: none; }
内容的提问来源于stack exchange,提问作者Robo Robok
相关产品推荐
相关产品推荐

