You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

touchstart事件监听器中无法调用preventDefault,如何兼顾被动监听与导航下拉交互逻辑?

touchstart事件监听器中无法调用preventDefault,如何兼顾被动监听与导航下拉交互逻辑?

这个问题我之前做移动端导航的时候也踩过坑!passive事件监听器的设计初衷就是让浏览器提前知道你不会调用preventDefault(),从而优化触摸/滚动的性能,但咱们这个交互逻辑又偏偏需要在第一次点击时阻止页面跳转,确实有点矛盾。不过有个很巧妙的解决方案,既能满足PageSpeed的性能要求,又能完美实现你的导航逻辑:

核心思路:拆分事件处理逻辑

状态切换默认行为拦截分开到两个不同的事件中:

  1. touchstart 事件只负责切换导航的展开状态,保持passive: true(避免性能警告),不调用preventDefault()
  2. 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

火山引擎 最新活动