I have this sketch application, which allows user to draw some pictures by pressing and moving the mouse.
/* 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
cell.addEventListener('dragstart', () => {}, false);
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('dragstart', () => {}, true);
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.
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;
}
<textarea class="canvasRow">16</textarea>
<textarea class="canvasColumn">16</textarea>
<div class="initCanvas">Create canvas</div>
<div class="canvas"></div>
According to MDN Web Docs: draggable, to prevent element from being dragged, I set the draggable
attribute of both canvas
and cell
to false
.
cell.draggable = false;
canvas.draggable = false;
But when I test it, in 1/20 or 1/30 of cases, a no-drop
cursor icon appears, and my mouseup
event will be swollen.
After check MDN Web Docs: HTML Drag and Drop API I find another suspicious event: dragstart
. So I add a listener on dragstart
event. Then debugger’s dragstart Event Listener Breakpoint
approved that dragstart
event was fired when bug occured.
In another previous question: Will “mousedown” and “mousemove” change the cursor icon and how to prevent it?, @skmail suggested me to add preventDefault()
on mousedown
event.
It works! dragstart
event is no longer fired. But I still want to know: why dragstart
event can still be triggered when elements are configured as draggable=false
? Did I mess something up in my code? According to MDN Web Docs: Drag Operations, user CANNOT drag an element if it is NOT draggable
.
When a user begins to drag, the dragstart event is fired.
It would be very nice if you can further explain why preventDefault()
can solve this problem? Thanks everyone.