如何在hashchange事件中阻止页面跳转至对应锚点
为什么你在hashchange里写preventDefault完全不生效
核心原因非常明确:hashchange 是浏览器完成地址栏hash更新、自动滚动定位到对应锚点之后,才抛出的通知类事件,这个事件本身的设计就不支持取消操作,这时候你在回调里调用e.preventDefault()相当于在行为完成后再喊停止,浏览器会直接忽略这个操作,自然达不到拦截效果。
正确的实现方案
根据你需要拦截的hash变更场景,可直接对应以下可落地方案:
1. 从链路层面完全阻断hash跳转
如果你需要连地址栏的hash都不更新、也不触发锚点滚动,直接监听hash变更的前置可取消事件:
注意:
beforehashchange是标准定义的可取消事件,触发时机早于hash变更和后续滚动逻辑,在这个事件中调用e.preventDefault()是唯一能从根源上阻断hash跳转的方案。
window.addEventListener('beforehashchange', (e) => { // 此处的preventDefault可真实生效,直接拦截hash变更和后续滚动行为 e.preventDefault(); // 可通过e.newURL拿到原本要跳转的hash值,执行自定义逻辑 const targetHash = e.newURL.split('#')[1]; // 比如执行tab切换、局部内容渲染等自定义交互,不触发页面滚动 });
2. 保留地址栏hash更新,仅阻止锚点自动滚动
如果你需要hash正常写入地址栏、也正常加入浏览器历史栈,只是不想让浏览器自动滚动到对应锚点位置:
- 页面初始化时直接关闭浏览器的自动滚动恢复能力,从根源上禁用hash触发的自动定位:
if ('scrollRestoration' in window.history) { // 强制浏览器不要自动恢复滚动位置,覆盖hash变更、前进后退触发的自动滚动逻辑 window.history.scrollRestoration = 'manual'; }
- 针对不支持
scrollRestoration的老版本浏览器,可在hashchange触发时将页面位置强制拉回变更前的位置:
// 提前记录hash变更前的滚动位置 let prevScrollY = window.scrollY; window.addEventListener('hashchange', () => { // 浏览器此时已自动滚动到锚点位置,直接将位置瞬间拉回 window.scrollTo({ top: prevScrollY, behavior: 'instant' // 禁用平滑滚动,避免用户感知到页面跳变 }); // 更新下一次需要记录的滚动位置 prevScrollY = window.scrollY; });
3. 适配你现有点击链接的拦截逻辑
你目前实现的a标签点击事件加e.preventDefault()的逻辑已经完全生效,这部分如果需要同步更新hash但不触发滚动,只需在点击回调里手动操作更新hash即可:
$('.parent').on('click', 'a', function(e) { e.preventDefault(); const targetHash = $(this).attr('href'); // 手动更新hash,由于已提前设置scrollRestoration为manual,不会触发自动滚动 history.pushState(null, '', targetHash); });
内容的提问来源于stack exchange,提问作者Livio
相关产品推荐
相关产品推荐

