优先用 pointerdown 替代 touchstart,因其兼容触控/鼠标/手写笔、无 300ms 延迟且原生支持键盘焦点和屏幕阅读器;若必须用 touchstart,需加 { passive: false } 并手动监听 Enter/Space。

touchstart 不能直接替代 click
直接把 click 换成 touchstart 会破坏键盘操作和屏幕阅读器支持——touchstart 不触发 Enter/Space,也不被 VoiceOver 自动识别为可交互元素。更糟的是,没加 { passive: false } 时,preventDefault() 无效,导致手势逻辑失控。
- 优先用
pointerdown:兼容触控/鼠标/手写笔,无 300ms 延迟,且原生支持键盘焦点和屏幕阅读器 - 若必须用
touchstart,绑定时加{ passive: false },并在回调里手动补keydown监听(监听Enter和Space) - 别在
document级绑touchstart,限定在具体容器内,避免事件冒泡干扰其他模块
touch-action 设置错会让菜单既拖不动又点不着
touch-action: none 是最常误用的值——它禁用所有原生交互,包括点击、滚动、焦点获取。结果是按钮点没反应,列表滑不动,连 button 的 :active 状态都失效。
- 下拉菜单或可滚动面板:用
touch-action: pan-y,允许上下滑动,但阻止左右拖拽和缩放 - 横向轮播区域:用
touch-action: pan-x,避免手指偏移时意外触发页面垂直滚动 - 纯按钮/开关区域:不设
touch-action,保持浏览器默认行为;设了反而坏事 - 真机测试方法:两指快速滑动菜单区域,看是否还透传到底部页面——透传说明没生效或设错了
滚动穿透不是 overflow: hidden 能解决的
给 body 加 overflow: hidden 表面看止住了背景滚动,实则引发三个问题:iOS Safari 滚动条消失后无法恢复、焦点丢失、页面重排闪屏。更麻烦的是,用户从菜单底部松手,惯性仍会带动背景滚动。
- 正确组合:菜单打开时,给
html元素加style="overscroll-behavior: none",阻断滚动链透传 - 同时给
body加touch-action: none,防止空白区触发背景滚动 - 记录
window.scrollY,关闭菜单后执行window.scrollTo(0, savedY),而不是靠 CSS 强制锁死 - 避免用
position: fixed; top: 0覆盖 body——它会切断滚动上下文,导致scrollIntoView失效
语义化标签才是手势友好底层支撑
很多“手势卡顿”实际是 DOM 层级或语义缺失造成的:用 div 冒充按钮,屏幕阅读器跳过它;嵌套超 6 层,touchstart 冒泡延迟飙升;viewport 缺失 user-scalable=no,Safari 死守双击检测窗口,300ms 延迟照旧。
立即学习“前端免费学习笔记(深入)”;
- 所有可点击区域必须用
<button></button>、<a href></a>或<input type="checkbox">,禁用role="button"伪装 -
label必须显式包裹input或用for关联,否则点击文字区域不触发 - DOM 深度控制在 ≤6 层,Chrome DevTools → Elements 面板右键 → “Show DOM properties” 查 depth 值
-
meta viewport必须含user-scalable=no或maximum-scale=1,否则 iOS Safari 不放弃双击判断逻辑
touch-action 设错、viewport 配漏、或者一个 div 按钮正在 silently 拒绝所有辅助技术。



















