介绍
正常拖拽文件到指定容器时,如果容器内部没有元素,可以正常响应enter和leave事件,如果内部具有元素,虽然只是在父级别容器时添加的事件,依然会导致子元素误触发,在拖拽时会频繁出现ondragenter和ondragleave事件,原因是冒泡导致。
复现
import react from 'react';
const Demo1 = ({ onDragEnter, onDragOver, onDragLeave, onDrop }) => {
const countRef = React.useRef(0);
const onFileDrop = (e) => {
if (e.type === 'drop') {
onDrop?.(e);
}
if (e.type === 'dragover') {
onDragOver?.(e);
}
if (e.type === 'dragenter') {
onDragEnter?.(e);
}
if (e.type === 'dragleave') {
onDragLeave?.(e);
}
};
return (
<div
class="parent"
onDrop={onFileDrop}
onDragOver={onFileDrop}
onDragEnter={onFileDrop}
onDragLeave={onFileDrop}
>
<div class="child">
<div class="sub-child">
拖拽到容器
</div>
</div>
</div>
)
}
分析
从外部移动到内部,事件触发顺序如下
- enter parent
- enter child
- leave parent
- enter sub-child
- leave child
抓取元素拖拽到容器内时,触发顺序是先enter下一个,再leave上一个,因此一个容器内,触发子元素,判断奇数和偶数即可过滤掉内部的误判断,取一个变量,在enter和leave时都递增,之后判断是奇数还是偶数来决定是否触发事件,leave后变量重置为0;
改进
import react from 'react';
const Demo2 = ({ onDragEnter, onDragOver, onDragLeave, onDrop }) => {
const countRef = React.useRef(0);
const onFileDrop = (e) => {
if (e.type === 'drop') {
onDrop?.(e);
}
if (e.type === 'dragover') {
onDragOver?.(e);
}
if (e.type === 'dragenter') {
countRef.current++;
if (countRef.current % 2) {
onDragEnter?.(e);
}
}
if (e.type === 'dragleave') {
countRef.current++;
if (!(lastActionRef.current % 2)) {
onDragLeave?.(e);
countRef.current = 0;
}
}
};
return (
<div
class="parent"
onDrop={onFileDrop}
onDragOver={onFileDrop}
onDragEnter={onFileDrop}
onDragLeave={onFileDrop}
>
<div class="child">
<div class="sub-child">
拖拽到容器
</div>
</div>
</div>
)
}
这样就可以精准以父容器边界来触发enter和leave事件了。