HTML onmousedown事件在移动端失效,长按触发prompt重复弹出怎么解决?
解决方案
问题根因
异常的核心原因有三个:
- 移动端长按元素会触发默认的上下文菜单、文本选中行为,打断事件流,导致抬起事件未触发、定时器未被清除
- 移动端pointer事件会同时触发原生触摸事件和模拟鼠标事件,导致触发逻辑被调用两次,生成两个计时器同时执行
- prompt弹窗弹出时会导致页面失焦,原本绑定的抬起事件不会触发,残留的计时器会在后续交互中重复执行
修复方案
1. HTML部分修改
替换原有事件绑定,新增阻止默认触摸行为、上下文菜单的配置:
<td> <button class="calculator-button" style="touch-action: none;" onclick="insert(0)" onpointerdown="activateMagic(event)" onpointerup="disableMagic()" onpointercancel="disableMagic()" oncontextmenu="return false;" > 0 </button> </td>
配置说明:
touch-action: none告知浏览器不对该元素做默认触摸处理(比如文本选择、缩放、上下文菜单)- 新增
onpointercancel处理触摸中断场景(比如按动时滑出按钮、来电打断等) oncontextmenu="return false"阻止右键/长按弹出系统菜单
2. JavaScript部分修改
新增触发锁,优化计时器逻辑,避免重复触发:
let answer = null; const input = document.getElementById("calcInput"); let pressTimer = null; // 新增弹窗锁,防止重复弹出prompt let isPromptOpen = false; const insert = (objToInsert) => { input.value += objToInsert; }; const clearInput = () => { input.value = null; answer = null; }; const activateMagic = (e) => { e.preventDefault(); // 已有计时器运行或弹窗已打开时直接返回 if (pressTimer || isPromptOpen) return; pressTimer = window.setTimeout(() => { isPromptOpen = true; answer = prompt("Enter the prank answer: "); // 弹窗关闭后重置状态,同时阻止本次长按触发click事件插入0 setTimeout(() => { isPromptOpen = false; e.target.onclick = () => { e.target.onclick = () => insert(0); }; }, 10); }, 2000); return false; }; const disableMagic = () => { clearTimeout(pressTimer); // 清空计时器引用,避免重复创建 pressTimer = null; }; const findAnswer = () => { // 修复原逻辑未设置自定义答案时会显示null的问题 if (answer == null) { let problemAnswer = eval(input.value); setTimeout(() => { input.value = problemAnswer; }, 10); } else { input.value = answer; } };
修改后桌面端、移动端均可正常运行,不会出现弹窗重复触发的问题。
内容的提问来源于stack exchange,提问作者DG27
相关产品推荐
相关产品推荐

