为什么draggable属性设置为false时仍然会触发drag和dragstart事件?

she*_*hen 5 html javascript css

我有这个草图应用程序,它允许用户通过按下和移动鼠标来绘制一些图片。

/* create grids in canvas */
const initCanvas = function() {
    const canvas = document.querySelector('.canvas');
    /* clear old canvas if any */
    canvas.innerHTML = '';
    /* create new canvas */
    let row = parseInt(document.querySelector('textarea.canvasRow').value);
    let column = parseInt(document.querySelector('textarea.canvasColumn').value);
    if (!row || !column) return;
    canvas.style['grid-template-columns'] = `repeat(${column}, 1fr)`;
    canvas.style['grid-template-rows'] = `repeat(${row}, 1fr)`;
    for (let i = 0; i < (row * column); i++) {
        let cell = document.createElement('div');
        cell.classList.add('cell');
        cell.draggable = false; // prevent cell from being dragged
        cell.addEventListener('mousedown', draw, false);
        cell.addEventListener('mouseenter', draw, false); // mouseenter is better than mouseover
        canvas.appendChild(cell);
    }
}

/* mousedown event handler on canvas */
const toDraw = function(e) {
    // e.preventDefault(); // !IMPORTANT: inhibit some browser's default drag & drop behaviors
    isDrawing = true;
}

/* mouseenter event handler on each cell */
const draw = function(e) {
    // e.preventDefault(); // !IMPORTANT: inhibit some browser's default drag & drop behaviors
    if (isDrawing) {
        this.style['background-color'] = color;
    }
}

/* mouseup event handler on whole document */
const stopDrawing = function(e) {
    isDrawing = false;
    e.stopPropagation();
}

/* variables */
let color = 'black';
let isDrawing = false;
/* canvas init */
const initCanvasBtn = document.querySelector('.initCanvas');
initCanvasBtn.addEventListener('click', initCanvas);
/* draw */
const canvas = document.querySelector('.canvas');
canvas.draggable = false; // prevent canvas from being dragged
canvas.addEventListener('mousedown', toDraw, true); // capture must be true. canvas must turn on "isDrawing" before cell capture the mousedown event.
document.addEventListener('mouseup', stopDrawing, true); // document element handle this. no need to notify children.
Run Code Online (Sandbox Code Playgroud)
textarea.canvasRow,
textarea.canvasColumn {
    width: 4rem;
    height: 1rem;
}

.initCanvas {
    width: 4rem;
    height: 2rem;
    border: 1px solid black;
}

.canvas {
    height: 50vw;
    width: 50vw;
    border: 1px solid black;
    display: grid;
}

.cell {
    background-color: #F5F5F5;
    border: 1px solid #EBEBEB;
}
Run Code Online (Sandbox Code Playgroud)
<textarea class="canvasRow">16</textarea>
<textarea class="canvasColumn">16</textarea>
<div class="initCanvas">Create canvas</div>
<div class="canvas"></div>
Run Code Online (Sandbox Code Playgroud)

根据MDN Web Docs:draggable,为了防止元素被拖动,我将和draggable的属性都设置为。canvascellfalse

cell.draggable = false;
canvas.draggable = false;
Run Code Online (Sandbox Code Playgroud)

但是我测试的时候,有1/20或者1/30的情况下,no-drop会出现光标图标,我的mouseup事件就会肿起来。这意味着drag operation发生了意外的事情。

在此输入图像描述

根据MDN Web Docs:HTML Drag and Drop API,我绑定了一个侦听器dragstartdrag事件。然后调试器Event Listener Breakpoint显示,当错误发生时,dragdragstart事件都被触发。 在此输入图像描述

在之前的另一个问题中:“mousedown”和“mousemove”会改变光标图标以及如何防止它?,@skmail 建议我添加preventDefault()事件mousedown

有用!drag事件dragstart消失了。但我还是想知道:为什么当元素配置为 时,仍然可以触发drag事件?dragstartdraggable=false我的意思是除了数百个空的之外几乎什么都没有div。而且它们都是不可拖动的`...我真的很困惑。

如果您能进一步解释为什么preventDefault()可以解决这个问题,那就太好了?目前,我唯一确定的是,这种意外情况drag operation是由浏览器的默认设置触发的。有人有什么提示吗?

Ald*_*ith 1

不幸的是,这种行为没有标准化。这是发生的事情:

当您第一次在画布上绘画时,将选择 div 内的(空)文本节点。浏览器单独处理选择,因此它们不受draggable 属性的控制。如果从选区内部拖动,则会触发拖动事件。preventDefault()onmousedown只是阻止这种选择的发生。

对于您的情况,更简单的解决方案是添加user-select: none;到 .canvas。(不要忘记添加webkit-Safari 浏览器)。

一个相关但更复杂的情况是当您在可拖动 div 顶部有一个文本输入时。在这种情况下,正确设置逻辑非常重要,这样选择拖动就不会被误解为正在拖动的 div。