必须用 touchstart 代替 click 实现即时响应,但 preventDefault() 仅在自定义滑动区域需配合 passive: false 使用;touch-action 应设为 manipulation/pan-x/pan-y 而非 none;DOM 深度勿超 3 层,触摸目标 clientWidth × clientHeight ≥ 48 × 48px。

touchstart 代替 click 是必须的,但别乱加 preventDefault
移动端点击延迟不是 bug,是浏览器为双击缩放预留的判断窗口;click 触发前平均卡 300ms,用户手指一抬就结束了,根本感知不到反馈。用 touchstart 能立刻响应,但加不加 preventDefault() 得看场景:
- 纯按钮类交互(如开关、菜单 toggle):绑定
touchstart后直接更新 UI 状态,不要调preventDefault()—— 否则某些 WebView(比如微信 X5 内核)会中断后续click或跳转逻辑 - 自定义滑动区域(如轮播容器、画布):必须加
{ passive: false }并在touchmove中调preventDefault(),否则默认滚动会抢走手势 -
touchstart里加preventDefault()多数时候没意义,还可能让 iOS Safari 忽略焦点或键盘唤起
touch-action 属性写错,比没写还危险
这个 CSS 属性直接告诉浏览器“别管我,默认手势全禁掉”,但写法不对就会让元素彻底失能:
-
touch-action: none用在按钮上 → 手指点下去毫无反应,click和touchstart全失效 -
touch-action: pinch-zoom单独使用 → 浏览器理解成“只允许缩放,禁止所有平移”,整个页面点不动、滚不了 - 正确做法:
touch-action: manipulation(按钮/卡片),touch-action: pan-x(横向轮播),touch-action: pan-y(列表页) - 可滚动容器(如
<div style="overflow: auto">)绝不能设none,否则滚动被锁死
DOM 深度超 6 层,touchstart 延迟翻倍
事件冒泡路径越长,从手指触达触发 handler 的耗时越不可控。低端安卓机上,6 层嵌套的 button 平均延迟 42ms,而语义化三层结构稳定在 8–12ms:
- 用 Chrome DevTools → Elements 面板右键任意节点 →
Show DOM properties查看depth值 - 别为套 class 强行加
<div>,改用display: contents抹掉无意义包裹层(Safari 15.4+ 支持) - 动态插入内容时,用
DocumentFragment批量 append,避免循环appendChild触发多次 layout - 所有带交互的元素必须用原生语义标签:
<button>替<div onclick>,<label for>包<input type="checkbox">
触摸目标尺寸 ≠ CSS padding,clientWidth 必须 ≥ 48px
屏幕阅读器和手指都依赖元素实际渲染尺寸,不是你看着像按钮就行:
立即学习“前端免费学习笔记(深入)”;
- 用 Chrome DevTools 切换设备模式,选中元素后看右下角
clientWidth × clientHeight数值,低于48 × 48就算不合格 - 图标按钮(如
<button><svg></svg></button>)必须显式设min-width: 48px; min-height: 48px;,SVG 自身尺寸不计入 - 相邻可触元素间距至少
8px,否则 VoiceOver 在滑动切换焦点时容易连跳两个 - 别用
font-size: 12px; padding: 2px 6px拼小按钮——算出来很可能不到 48px,且 TalkBack 会读作“静态文本”



















