What could be causing my mousedown event to be activated during a mouseup event?

I'm currently tackling the Etch-a-Sketch project within the Odin Project curriculum, and I've encountered some peculiar behavior while implementing the mousedown and mouseup event listeners.

Within a container div, I have established a 50x50 grid of divs. The container div responds to mousedown events by triggering the startDrawing function, which fills the boxes as the user hovers over them. Similarly, it listens for mouseup events, halting the filling of boxes when the mouse is released.

Everything seems to be functioning smoothly, however, at times, when drawing a line with the left mouse button held down, the box div appears to get "grabbed." Subsequently, dragging the mouse without filling the boxes occurs until the mouse is released, after which normal drawing resumes. It's almost like a toggle switch of sorts after the accidental "grabbing," but reverts back to normal behavior after the next mousedown event.

The issue may be easier to grasp visually, so below you'll find my code snippet along with a link to the corresponding Codepen demonstrating the Etch-a-Sketch project.

I've attempted searching for methods to remove this "grabbing" behavior online, but haven't had much luck, likely because I'm unsure of the appropriate keywords to use.

If anyone can shed light on what's occurring and offer guidance on resolving this, I would greatly appreciate it.

Etch-a-Sketch Codepen

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>
    <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>

Answer №1

In this scenario, when a mousedown event is followed by a mousemove event, the default action is to trigger a grab operation.
At some point, the browser will select content on the page and initiate grabbing it.

To prevent this default behavior, you can notify the browser that your code will handle the event and it should not execute its usual actions. This can be done by using 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(); // Notify browser event is handled
    this.classList.add('filled');
}

function clearGrid(){
    const boxes = document.querySelectorAll('.box');
    boxes.forEach(box => box.classList.remove('filled'));
}

function startDrawing(evt){
    evt.preventDefault(); // Notify browser event is handled
    const boxes = document.querySelectorAll('.box');
    boxes.forEach(box => box.addEventListener('mouseover', fillBox));
}

function stopDrawing(evt){
    evt.preventDefault(); // Notify browser event is handled
    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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating an HTML slideshow banner: What you need to know

I am looking to create a slideshow for my banner that changes automatically. The banners are located in a folder, and I want the website to display them one by one without manual intervention. Currently, I have managed to display the images from the folder ...

Creating a shimmering glow for a dynamic AJAX div block in real-time

I created an Ajax code that retrieves results from a txt file in real time, which are then automatically displayed in a div block using the following lines: if (xmlhttp.responseText != "") { InnerHTMLText = xmlhttp.responseText + document.getElementBy ...

No cookie found in the system

Attempting to create an effect using bloom and shaders in post-processing. However, encountering an error in the console with a blank white screen. I have tried clearing cookies, caches, and even running this in incognito mode, but it's still not work ...

Setting the text alignment using the innerHTML property of CSS

Having difficulty aligning text with innerHTML I am trying to create a link element using JavaScript, but I cannot get the text to align to the center. Here is my code: a= document.createElement('a'); a.style.cssText= 'font-size: 20px; curs ...

Leveraging React to efficiently connect with friends on Firebase Realtime Database, enhancing the capability to include multiple connections

Currently, I am working on a project that involves React and Firebase's real-time database to create a friend network feature. The main challenge I'm facing is when a user enters an email into an input field. Upon submission, the system takes a s ...

What is the best method for aggregating multiple JSON responses into a single array?

I am struggling to get my data in the desired format of [{..},{..},{..}]. I have created an empty array using const arr = [], but when I push all JSON data into this array, the output shows as separate arrays. How can I fix this issue? // const arr = [] co ...

Instructions for activating a button in the absence of any inputs

I need help enabling a button in angularjs based on whether any of the inputs are filled out. I was successful in disabling a button when only one input is checked, but how can I do this for all inputs affecting one button? This is what I've attempted ...

Is there a way to automatically render Katex without the need for double dollar signs?

I've been facing difficulties grasping the concept of rendering Katex without relying on $$ before and after the math expression. According to Katex's repository on GitHub, I should use the following code: <script> renderMathInElement( ...

Is it possible to add padding to an HTML element without affecting its overall dimensions?

Is it possible to apply padding to a div without increasing its size? <div id="someDiv"> someContent </div> #someDiv{ padding: 1em; } One potential solution is to add another nested div within #someDiv and apply margin to that, rathe ...

Leveraging basscss through NPM/Webpack installation

As a newcomer to the webpack/react app environment, I am attempting to incorporate the Basscss CSS framework into my project. After successfully running 'npm i basscss' and adding require('basscss/css/basscss.css'); to my app entry poin ...

Exploring the ideal scenarios for utilizing propTypes in React

When creating in-house components that require specific props to function properly, I believe it is best to conduct prop requirement checks during testing rather than including propTypes in the production code. This is especially important for components t ...

Using .attr() to change the 'id' in jQuery will not be effective

Initially, the code has been streamlined to include only the necessary components. Here is the HTML file (single_lesson.html) in question: <tr class="my_lessons"> <td> <select name="my_von" id="my_von"></select> &l ...

What is the best way to save information from an ng-repeat loop into a variable before sending it to an API?

My goal is to store the selected value from ng-repeat in UI (user selection from dropdown) and assign it to a variable. function saveSelection() { console.log('inside function') var postToDatabase = []; vm.newApplicant.values ...

Cut off all information beyond null characters (0x00) in Internet Explorer AJAX responses

When using Internet Explorer (IE6, IE7, and IE8), null characters ("0x00") and any subsequent characters get removed from ajax responses. Here's the code snippet that showcases a loop of AJAX requests: var pages = 10; var nextnoteid = 0; for (isub ...

If the user decides to change their answer, the current line between the two DIVs will be removed

After clicking on two DIVs, I created two lines. Now, I am facing an issue with resetting the unwanted line when changing my answer. To reset the line, you can refer to the code snippet below: var lastSelection; ...

Create a function that reverses the process of removing mesh

I have a function that creates mesh when you click on another. Upon clicking, 2 or 3 meshes are created and move to their positions. Now, I want to implement the reverse function: when the meshes are deployed and you click on them again, the previously cre ...

Removing white spaces from response HTML in JBoss 5.1 version

Does anyone know of a JBoss 5.1 plugin or utility that can automatically strip leading and trailing white spaces from HTML being sent as a response? Similarly, is there a way to do this for JSP files upon deployment? Any application-specific settings would ...

Arrange the columns in Angular Material Table in various directions

Is there a way to sort all columns in an Angular material table by descending order, while keeping the active column sorted in ascending order? I have been trying to achieve this using the code below: @ViewChild(MatSort) sort: MatSort; <table matSort ...

Having trouble understanding why getStaticProps function is not loading before the main exported function

When I use npm run dev to troubleshoot this issue, it utilizes getStaticProps to process various d3 properties before injecting them into the main output function during runtime. However, it seems that getStaticProps is not running as expected - a consol ...

Using jQuery to generate a date based on user input values

I'm in need of some assistance to complete this task I am currently developing a script that imports data from an input field that appears like this: <input type='text' class='hidden' value='$a' id='test'&g ...