Reputation: 5663
I've been working on the etch-a-sketch project of the Odin project, and I came across some weird behavior when implementing the mousedown
and mouseup
event listeners.
I've created a 50x50 grid of divs in a container div. The container div listens on mousedown
events, upon which it calls the startDrawing
function, filling the boxes that the user hovers over. It also listens on mousedown
events, so that when the mouse is released, the stopDrawing
function is called and the filling of boxes stops.
All of this works pretty much fine, but sometimes when I start drawing a line with the mouse left button held down, the box div becomes "grabbed". After this, while dragging the mouse with the left button still down, the boxes are not filled when hovering over them. Then when I release the mouse it starts drawing. It's as if the behavior is toggled after the accidental "grabbing", but on the next mousedown it starts acting normally again.
This is probably harder to explain than to see it for yourself, so below is my code as well as a link to a corresponding codepen.
I've tried googling to find out how I can remove this "grabbing" behavior, but I haven't really found anything, probably because I don't even know what keywords to search for.
Can somebody explain what's happening and provide some info on how I can fix this?
const GRID_SIZE = 50;
for(let i = 0; i < GRID_SIZE * GRID_SIZE; i++){
const container = document.getElementById('container');
let div = document.createElement('div');
div.classList.add('box');
container.appendChild(div);
}
function fillBox(e){
this.classList.add('filled');
}
function clearGrid(){
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => box.classList.remove('filled'));
}
function startDrawing(){
// console.log("start drawing");
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => box.addEventListener('mouseover', fillBox));
}
function stopDrawing(){
// console.log("stop drawing");
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => box.removeEventListener('mouseover', fillBox));
}
const container = document.querySelector('#container');
container.addEventListener('mousedown', startDrawing);
container.addEventListener('mouseup', stopDrawing);
const button = document.querySelector('#clear-grid-btn');
button.onclick = clearGrid;
#container{
width: 500px;
display: grid;
grid-template-columns: repeat(50, 10px);
grid-template-rows: repeat(50, 10px);
border: solid;
border-color: black;
margin:auto;
}
.box{
width: 10px;
height: 10px;
}
.box:hover{
background-color: blue;
}
.filled{
background-color: blue;
}
#clear-grid-btn{
display:block;
margin:auto;
margin-top: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="etch-a-sketch.css">
</head>
<body>
<div id="container"></div>
<button id="clear-grid-btn">Clear grid</button>
</body>
<script src="etch-a-sketch.js"></script>
</html>
Upvotes: 3
Views: 1672
Reputation: 137123
What happens here is that the default behavior of a mousedown + mousemove is to initiate a grab.
Certainly, at some point, the browser will select some content in the page and start grabbing it.
The solution to avoid this is to tell the browser that your code does handle the event and should thus not perform its usual behavior. You can do so by calling the Event::preventDefault()
method:
const GRID_SIZE = 50;
for(let i = 0; i < GRID_SIZE * GRID_SIZE; i++){
const container = document.getElementById('container');
let div = document.createElement('div');
div.classList.add('box');
container.appendChild(div);
}
function fillBox(evt){
evt.preventDefault(); // tell the browser we handle that event
this.classList.add('filled');
}
function clearGrid(){
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => box.classList.remove('filled'));
}
function startDrawing(evt){
evt.preventDefault(); // tell the browser we handle that event
// console.log("start drawing");
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => box.addEventListener('mouseover', fillBox));
}
function stopDrawing(evt){
evt.preventDefault(); // tell the browser we handle that event
// console.log("stop drawing");
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => box.removeEventListener('mouseover', fillBox));
}
const container = document.querySelector('#container');
container.addEventListener('mousedown', startDrawing);
container.addEventListener('mouseup', stopDrawing);
const button = document.querySelector('#clear-grid-btn');
button.onclick = clearGrid;
#container{
width: 500px;
display: grid;
grid-template-columns: repeat(50, 10px);
grid-template-rows: repeat(50, 10px);
border: solid;
border-color: black;
margin:auto;
}
.box{
width: 10px;
height: 10px;
}
.box:hover{
background-color: blue;
}
.filled{
background-color: blue;
}
#clear-grid-btn{
display:block;
margin:auto;
margin-top: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="etch-a-sketch.css">
</head>
<body>
<div id="container"></div>
<button id="clear-grid-btn">Clear grid</button>
</body>
<script src="etch-a-sketch.js"></script>
</html>
Upvotes: 4