Firefox中mousedown异步等待后offsetX/offsetY变为0的原因
跨浏览器事件offsetX属性差异问题
问题复现代码
JavaScript 代码
const $test = document.querySelector('.test'); function sleep() { return new Promise(resolve => { setTimeout(() => { resolve(); }, 300); }); } $test.addEventListener('mousedown', async e => { console.log(e.offsetX, e.offsetY); // 正常场景下为非0值,例如(90, 163) await sleep(); console.log(e.offsetX, e.offsetY); // Firefox环境下返回0 0 });
CSS 样式
#app { position: relative; width: 100vw; height: 100vh; } .test { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 300px; height: 300px; background-color: aqua; }
HTML 结构
<div id="app"> <div class="test"></div> <!-- <canvas width="300" height="300" class="test"></canvas> --> </div>
异常表现
上述代码在Chrome浏览器中运行正常,在Firefox浏览器中存在异常:
- 执行sleep异步等待前,读取mousedown事件对象e的offsetX、offsetY属性为正常非0值;
- 经过300ms的await等待后,再次读取e.offsetX、e.offsetY的值变为0。
差异产生原因
这个跨浏览器表现差异,根源是两个浏览器对DOM事件对象属性的存储、计算策略不同:
- 先明确基础规则:
offsetX/offsetY属于计算属性,作用是返回鼠标指针相对于事件目标元素内边距边界的偏移值,本身不是事件触发时就写死在对象上的固定值。 - Chrome的实现逻辑:mousedown这类用户交互事件进入同步派发流程时,会第一时间计算
offsetX/offsetY的结果,把值缓存到事件对象上。哪怕后续用await跳出了原生事件的同步调用栈,异步阶段读取属性时拿到的还是之前缓存的固定值,不会出现变0的问题。 - Firefox的实现逻辑:Firefox不会主动缓存这两个计算属性的值,只有当代码还处在事件原生同步派发的调用栈里时,读取属性才会实时拉取鼠标坐标、元素边界等数据计算返回正确结果。一旦await让出执行线程,代码脱离了最初的事件同步上下文,事件关联的原生临时数据(包括触发瞬间的鼠标位置、目标元素尺寸位置等计算必需的原始参数)就会被回收,此时再读取
offsetX/offsetY拿不到计算依据,就会直接返回默认值0。
这个问题的规避方式非常简单:如果要在异步逻辑里用到这两个坐标值,在await执行前就把
e.offsetX、e.offsetY的值赋值给普通变量保存,后续用变量里存的值即可,这种写法在两个浏览器下表现完全一致。
内容的提问来源于stack exchange,提问作者panda
相关产品推荐
相关产品推荐

