如何区分tap与click事件实现带二级子菜单的导航触屏适配?
可行实现方案
核心思路
放弃分开监听touch、mouse事件的旧方案,改用W3C标准的Pointer Events API统一处理所有输入类型,直接通过事件属性判断是鼠标点击还是触屏tap,完美兼容混合设备(触屏笔记本、平板等)的场景。
为什么你之前的方案失效
- 目前主流Chromium内核浏览器对
touchstart事件默认启用passive模式,混合设备(触屏笔记本)默认不会触发touch事件,只会触发标准化的Pointer事件 - 触屏触发的模拟click事件之前确实会触发mouseenter/mousemove等模拟鼠标事件,导致基于鼠标事件的设备检测完全不可靠
具体实现
1. 保留原有CSS hover逻辑(兼容鼠标设备)
/* 原有hover逻辑不用修改 */ .menu-link .submenu { display: none; } .menu-link:hover .submenu { display: block; } /* 新增触屏触发的展开类 */ .menu-link.touch-submenu-expanded .submenu { display: block; }
2. JS逻辑实现触屏设备首次tap展开、二次tap跳转
const allMenuLinks = document.querySelectorAll('.menu-link'); allMenuLinks.forEach(menuLink => { // 状态标记:子菜单是否已通过触屏展开 let isSubmenuExpanded = false; // 状态标记:当前触发的输入是否为触屏 let isTouchInput = false; // 监听pointerdown事件判断输入类型 menuLink.addEventListener('pointerdown', (e) => { isTouchInput = e.pointerType === 'touch'; }); // 监听click事件处理交互逻辑 menuLink.addEventListener('click', (e) => { // 鼠标输入直接走默认跳转逻辑,不做额外处理 if (!isTouchInput) return; // 首次触屏点击:阻止跳转,展开子菜单 if (!isSubmenuExpanded) { e.preventDefault(); menuLink.classList.add('touch-submenu-expanded'); isSubmenuExpanded = true; // 点击菜单外区域自动收起子菜单,重置状态 const closeSubmenu = (outerClickEvent) => { if (!menuLink.contains(outerClickEvent.target)) { menuLink.classList.remove('touch-submenu-expanded'); isSubmenuExpanded = false; document.removeEventListener('click', closeSubmenu); } }; document.addEventListener('click', closeSubmenu, { once: true }); } // 二次触屏点击:不阻止默认事件,直接触发主菜单链接跳转 }); });
补充:检测设备是否为鼠标设备的原生方法
如果需要提前检测设备输入类型,不需要触发事件也可以用CSS媒体查询实现:
// 返回true表示当前主输入设备为精确指针(鼠标),false为触屏/笔等非精确输入 const isMousePrimaryDevice = window.matchMedia('(pointer: fine)').matches;
这个方法也可以直接在CSS中使用,不需要依赖JS:
/* 仅鼠标设备生效的样式 */ @media (pointer: fine) { /* 你的样式 */ }
内容的提问来源于stack exchange,提问作者Lenny
相关产品推荐
相关产品推荐

