touchstart事件监听器中无法调用preventDefault,如何兼顾被动监听与导航下拉交互逻辑?
touchstart事件监听器中无法调用preventDefault,如何兼顾被动监听与导航下拉交互逻辑?
这个问题我之前做移动端导航的时候也踩过坑!passive事件监听器的设计初衷就是让浏览器提前知道你不会调用preventDefault(),从而优化触摸/滚动的性能,但咱们这个交互逻辑又偏偏需要在第一次点击时阻止页面跳转,确实有点矛盾。不过有个很巧妙的解决方案,既能满足PageSpeed的性能要求,又能完美实现你的导航逻辑:
核心思路:拆分事件处理逻辑
把状态切换和默认行为拦截分开到两个不同的事件中:
touchstart事件只负责切换导航的展开状态,保持passive: true(避免性能警告),不调用preventDefault();click事件里根据导航的展开状态,决定是否阻止默认跳转(因为click事件监听器默认支持preventDefault(),没有passive的限制)。
这样修改后,触摸设备上的操作流程是:
- 第一次触摸:
touchstart触发,给父元素添加expand类 → 紧接着触发click事件,检测到expand类存在,调用preventDefault()阻止跳转; - 第二次触摸:
touchstart触发,移除expand类 → 紧接着触发click事件,检测到expand类不存在,允许页面正常跳转。
修改后的代码示例
document.querySelectorAll("#primary-nav>ul>li.menu-item-has-children>a").forEach(elem => { // touchstart:仅处理导航展开/收起,使用passive优化性能 elem.addEventListener('touchstart', () => { const parent = elem.parentElement; parent.classList.toggle('expand'); }, { passive: true }); // click:根据导航状态决定是否阻止跳转 elem.addEventListener('click', (e) => { const parent = elem.parentElement; if (parent.classList.contains('expand')) { // 第一次点击(展开状态),阻止跳转 e.preventDefault(); } else { // 第二次点击(收起状态),允许跳转,同时可以手动收起(可选) parent.classList.remove('expand'); } }); });
额外说明
- 这个方案同时兼容鼠标设备:鼠标点击时,
click事件会直接处理展开/收起逻辑,不需要额外修改; - 完全避开了
passive监听器中不能调用preventDefault()的限制,也不会有PageSpeed的性能警告; - 如果你的导航在鼠标hover时已经有展开逻辑,也可以保留,这个代码只会在点击时生效,互不冲突。
你可以试试这个方案,应该能完美解决你的问题!
备注:内容来源于stack exchange,提问作者wsSteve




