Web前端在拖拽时dom的style与获取的left值不一致
Max Zhang Lv4

场景

在拖拽时,获取到的 dom 的 style 与实际的 style 不一致。在拖拽 dom 时,想制作出拖拽一个 dom 时,其他 dom 会根据拖拽 dom 的位置进行相应的移动,并且在边界处会有一个阻力,即拖拽 dom 不能超出边界。

在实际开发中,我使用了监测拖拽 dom 的方向与位移,获取一个方向向量,当有一个 dom 到达某个边界时,就会改变这个方向向量的方向,从而达到阻力的效果。

最开始时候,我将所有的 dom 元素都作为临时变量存储在一个数组中,当拖拽 dom 的位置发生变化时,就会遍历这个数组,根据拖拽 dom 的位置,计算出其他 dom 的位置,并且将这些 dom 的位置进行更新。

所以在检验是否到达边界时,我是根据 dom 的 style 来进行判断的,即当 dom 的 style.left 值小于 0 时,就会将方向向量的 x 值变为正数,从而达到阻力的效果。

结果发现,dom 的 style.left 值和实际的 left 值不一致,即使我在 console.log 中打印的 dom 元素的 style.left 值也不一致。

1
2
3
4
console.log("offsetLeft:", dom.offsetLeft, dom);

// offsetLeft: 100
// <div style="left: 30px;"></div>

原因

我是在拖拽 dom 的 mousemove 事件中对其他 dom 进行位置的更新的,而在 mousemove 事件中,虽然我已经对拖拽 dom 的位置进行了更新,但是浏览器并没有对 dom 的位置进行更新,所以在 mousemove 事件中获取到的 dom 的 style.left 值并不是最新的。

如果想确保特定的 DOM 元素已完成渲染,可以利用 requestAnimationFrame 函数和 Promise。

1
2
3
4
5
6
7
8
9
10
11
12
function waitForElementToRender(selector: string): Promise<HTMLElement> {
return new Promise((resolve) => {
const element = document.querySelector<HTMLElement>(selector);
if (element) {
resolve(element);
} else {
requestAnimationFrame(() => {
resolve(waitForElementToRender(selector));
});
}
});
}

但是这会导致整个 move 事件的执行全部会变成异步的,并且会导致拖拽的卡顿。

解决方案

后来我意识到,所有 mousemove 事件中都不应当获取自己主动更新的 dom 的 style,而应当在 mouseup 事件中获取,因为 mouseup 事件中的 dom 的 style 是最新的。

也就是说,我应当在 mousemove 事件中,只更新 dom 的位置,而不应当获取 dom 的 style。而在 mouseup 事件中,获取 dom 的 style,然后根据 style 来判断是否到达边界。使用 mouseup 事件中的 style 以及拖拽过程中的方向向量,就可以计算出拖拽 dom 到达边界时的位置。

所有在 mousemove 事件的处理过程中,都不应该保留中间状态的节点,应当用当前状态与起始状态做运算,来处理业务。

 评论
评论插件加载失败
正在加载评论插件