navigator.vibrate()必须在pointerdown等用户手势同步上下文中直接调用,仅支持数字或偶数长度数组参数,iOS完全不支持,国产安卓常“假支持”,需真机触摸验证。

按钮点击必须同步调用 navigator.vibrate()
绝大多数“点不动”不是代码写错,而是震动被浏览器静默拦截——navigator.vibrate() 只在用户手势事件的**同步执行上下文**中有效。一旦放进 setTimeout、fetch().then()、await 或页面加载完成回调里,就彻底失效(不报错、不震动)。
✅ 正确写法:直接绑定到 click 或更可靠的 pointerdown 事件处理器内,且调用不能延迟:
button.addEventListener('pointerdown', () => {
navigator.vibrate(150); // 必须是这行,不能包在 Promise 或定时器里
});
-
pointerdown比click更早触发,兼容触摸和鼠标,避免因跳转或防抖中断震动 - 不要用
touchstart—— iOS Safari 在某些版本中会延迟触发或与滚动冲突 - 如果按钮是动态插入的,确保事件监听在元素存在后立即绑定,而非依赖事件委托(部分 WebView 对委托支持不稳定)
navigator.vibrate() 参数怎么传才不被静默忽略
类型或结构不对,API 会返回 true 却毫无震动——这不是 bug,是规范行为。
- ✅ 接受:
navigator.vibrate(200)(单次 200ms),或navigator.vibrate([300, 100, 200])(震 300ms → 停 100ms → 震 200ms) - ❌ 拒绝:
navigator.vibrate("200")(字符串)、navigator.vibrate([100, 200, 300])(奇数长度数组,末项被丢弃)、navigator.vibrate([0, 100])(部分 Android WebView 会截断为[100]或直接忽略) - ⚠️ 数组总时长不能超过 5000ms;建议项数 ≤ 5,否则部分安卓 WebView 可能报
InvalidStateError或截断
iOS 设备上根本不会震,别白费力气
Safari 不开放硬件震动控制权限,所谓 navigator.vibrate 在 iOS 上始终返回 undefined 或调用后无响应——连 webkitVibrate 这类旧别名也早已失效。
立即学习“前端免费学习笔记(深入)”;
- 没有标准 fallback 方案;伪造震动音效(比如播放“嗡”声)既失真又干扰用户,尤其在静音模式下突兀出声
- 检测方式只能靠 UA 或
matchMedia('(prefers-reduced-motion: reduce)'),但无法 100% 确认是否为 iOS + 支持触感反馈 - 真正可行的路径只有两个:放弃 iOS 震动,纯视觉反馈(如按钮缩放 + class 切换);或转原生封装(Capacitor/Cordova)
国产安卓系统经常“假装支持”
华为 EMUI、小米 MIUI 等定制系统常让 navigator.vibrate 存在且返回 true,但实际无震动——这不是兼容性问题,是系统级屏蔽。
- Chrome DevTools 的 Sensors 面板可模拟震动,但仅验证 JS 逻辑是否触发,不反映真实设备行为
- 必须实机测试:用真机点按钮,同时用手摸机身确认是否震;光看控制台返回值毫无意义
- 对关键操作(如表单提交失败),优先用短脉冲(100–200ms);长震动(>1000ms)在多数国产机型上会被强制截断甚至禁用
pointerdown 里直接调用,或没在真机上摸过机身确认,就等于没做。



















