Im making an etch sketch project using mainly vanilla js. I've just changed the event handling the coloring of cells from just mouseover to only coloring cells when the user is holding down the mouse, but doing so seems to have affected the slider that determines the grid size for some reason.
If you move the slider around quick enough it struggles to keep up with the changing grid size slowing down the whole site preventing you from being able to draw and sometimes even freezing it.
Coloring cells with just mouse over:
//query selector for the grid container (the grids parent container)
const gridContainer = document.querySelector(".grid-container");
let cellColor = "black";
//an object to store hex colors and their names
const colors = {
White: "#FFFFFF",
Black: "#000000",
Red: "#FF2D00",
Blue: "#002BFF",
LightSalmon: "#FFA07A",
IndianRed: "#CD5C5C",
DarkRed: "#8B0000",
LightGreen: "#90EE90 ",
MediumSpringGreen: "#00FA9A ",
MediumSeaGreen: "#3CB371",
paleTurquoise: "#40E0D0",
aqua: "#00FFFF",
SteelBlue: "#4682B4",
};
const gridWidth = gridContainer.offsetWidth;
//function to return a nodelist of all of the nodes that have an id of cell
const selectCell = () => {
return (cellQuerySelectAll = document.querySelectorAll("#cells"));
};
//function to wipe grid
const wipeGrid = () => {
gridContainer.innerHTML = "";
};
const resetGridColor = () => {
const gridCells = selectCell();
gridCells.forEach((cell) => {
cell.style.backgroundColor = "";
});
};
const colorCell = (cell, color) => {
return (cell.style.backgroundColor = `${color}`);
};
//function used to create grid with the parameter being passed in being how many cells the user wants
const createGrid = (numberOfCells) => {
//create a div that is a colum which will each hold a row of cells.
const col = document.createElement("div");
//setting the class for col so that it becomes a flex col (check css => col)
col.className = "col";
//for loop used to append rows of cells to col div
for (let y = 0; y < numberOfCells; y++) {
//create a div that will be a row of cells
const row = document.createElement("div");
//setting the class for row so that it becomes a flex row (check css => row)
row.className = "row";
//for loop used to append cells to row div
for (let x = 0; x < numberOfCells; x++) {
//create a div that is the cell for the grid
const cell = document.createElement("div");
//setting the styling for the cells
//width and height are set dynamicallt based on the grid containers width and how many cells the user wants
cell.style.cssText = `width:${gridWidth / numberOfCells}px; height:${
gridWidth / numberOfCells
}px; `;
cell.id = "cells";
cell.addEventListener("mousemove",() => {
colorCell(cell, cellColor);
});
row.appendChild(cell);
}
col.appendChild(row);
}
gridContainer.appendChild(col);
};
const createColors = () => {
//for loop to get key in colors object
for (const colorName in colors) {
//create div for each color
const color = document.createElement("div");
color.id = `${colorName}`;
color.classList.add("color");
//styling for each color
color.style.cssText = `width:30px; height:30px; border-radius:50%; background-color:${colors[colorName]}; display:flex; justify-content:center;`;
//when user clicks on color changes global color var to hex color selected
color.addEventListener("click", () => {
cellColor = `${colors[colorName]}`;
});
const colorContainer = document.querySelector(".colors-container");
//apend colors to the color container
colorContainer.appendChild(color);
}
};
function ready(callback) {
// in case the document is already rendered
if (document.readyState != "loading") callback();
else if (document.addEventListener)
document.addEventListener("DOMContentLoaded", callback);
else
document.attachEvent("onreadystatechange", function () {
if (document.readyState == "complete") callback();
});
}
//function only runs once
ready(function () {
//query selector for slider used to control grid size
const slider = document.getElementById("slider");
//need to orginally make the grid once by default as the code below only creates a grid once there is input on the slider
createGrid(1);
// setCellEventListners();
createColors();
//event listner on the slider that executes the call back function inside whenver anyinput occurs on the slider
slider.addEventListener("input", () => {
const sliderValue = document.querySelector(".thumb-value");
sliderValue.innerHTML = `${slider.value}`;
//wipe the current grid so that the new grid does't just get added to the bottom of the current grid. Comment this line out
//and observe what happens when the slider is moved.
wipeGrid();
createGrid(slider.value);
// setCellEventListners();
});
//event listner to reset all cells on grid to having no bg color
const reset = document.querySelector("button");
reset.addEventListener("click", () => {
resetGridColor();
});
});
* {
box-sizing: border-box;
padding: 0;
margin: 0;
font-family: 'Roboto', sans-serif;
--thumbNumber: '5';
}
.h-100vh {
height: 100vh;
}
.center-me {
display: flex;
align-items: center;
justify-content: center;
}
.grid-container {
border: 0.1px solid black;
height: 400px;
width: 400px;
}
.col {
display: flex;
flex-direction: column;
}
.row {
display: flex;
}
.gap-2em {
gap: 2em;
}
.gap-1em {
gap: 1em;
}
.title {
font-size: 7em;
margin:auto;
}
button {
border:none;
height: 50px;
border-radius: 5px;
background-color: #1682FF;
color:white;
}
button:hover {
background-color: #A0CCFF;
}
.input-label {
font-size: 1.25rem;
}
.colors {
display:flex;
flex-direction: column;
height: 100%;
background-color: rgba(210, 210, 210, 0.1);
border-radius: 10px;
padding: 10px;
justify-content:center;
}
.slider {
-webkit-appearance: none;
width: 100%;
height: 25px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 1;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider:hover {
opacity: 0.7;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
border-radius: 5px;
background: #1682FF;
cursor: pointer;
}
.slider::-webkit-slider-thumb::before {
content: var(--thumbNumber);
}
.range-div {
display:flex;
align-items: center;
gap:0.25em;
}
.colors-container {
width: 270px;
display:flex;
flex-direction: row;
flex-wrap: wrap;
row-gap:30px;
column-gap:20px ;
justify-content:center;
align-items: center;
}
.color-label {
justify-content: space-evenly;
}
.color:hover {
border:none;
outline-offset: 2px;
outline: 2px solid #1682FF;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Etch a Sketch</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&family=Space+Mono&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="center-me h-100vh">
<div class="col gap-2em">
<p class="title">Etch a Sketch</p>
<div class="row gap-2em">
<div class="grid-container"></div>
<div class="col gap-1em">
<div class="slidecontainer col gap-1em">
<label for="slider" class="input-label"> GRID SIZE</label>
<div class="range-div">
<div class="thumb-value input-label">1</div>
<input
name="slider"
type="range"
min="1"
max="100"
value="1"
class="slider"
id="slider"
/>
</div>
</div>
<button class="input-label">RESET</button>
<div class="gap-1em colors">
<p class="input-label row gap-1em color-label">COLORS</p>
<div class="center-me">
<div class="colors-container"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js" async defer></script>
</body>
</html>
Coloring cells only when the user is holding the mouse down: (CODE IM HAVING AN ISSUE WITH)
//query selector for the grid container (the grids parent container)
const gridContainer = document.querySelector(".grid-container");
let cellColor = "black";
//an object to store hex colors and their names
const colors = {
White: "#FFFFFF",
Black: "#000000",
Red: "#FF2D00",
Blue: "#002BFF",
LightSalmon: "#FFA07A",
IndianRed: "#CD5C5C",
DarkRed: "#8B0000",
LightGreen: "#90EE90 ",
MediumSpringGreen: "#00FA9A ",
MediumSeaGreen: "#3CB371",
paleTurquoise: "#40E0D0",
aqua: "#00FFFF",
SteelBlue: "#4682B4",
};
const gridWidth = gridContainer.offsetWidth;
//function to return a nodelist of all of the nodes that have an id of cell
const selectCell = () => {
return (cellQuerySelectAll = document.querySelectorAll("#cells"));
};
//function to wipe grid
const wipeGrid = () => {
gridContainer.innerHTML = "";
};
const resetGridColor = () => {
const gridCells = selectCell();
gridCells.forEach((cell) => {
cell.style.backgroundColor = "";
});
};
const colorCell = (cell, color) => {
return (cell.style.backgroundColor = `${color}`);
};
//function used to create grid with the parameter being passed in being how many cells the user wants
const createGrid = (numberOfCells) => {
//create a div that is a colum which will each hold a row of cells.
const col = document.createElement("div");
//setting the class for col so that it becomes a flex col (check css => col)
col.className = "col";
//for loop used to append rows of cells to col div
for (let y = 0; y < numberOfCells; y++) {
//create a div that will be a row of cells
const row = document.createElement("div");
//setting the class for row so that it becomes a flex row (check css => row)
row.className = "row";
//for loop used to append cells to row div
for (let x = 0; x < numberOfCells; x++) {
//create a div that is the cell for the grid
const cell = document.createElement("div");
//setting the styling for the cells
//width and height are set dynamicallt based on the grid containers width and how many cells the user wants
cell.style.cssText = `width:${gridWidth / numberOfCells}px; height:${
gridWidth / numberOfCells
}px; `;
cell.id = "cells";
//var to store state of mouse
let mouseDown = false;
//event listener on whole grid to see if mouse is down on it (not just individual cells)
gridContainer.addEventListener("mousedown", () => {
mouseDown = true;
//event listener on each cell to see if mouse is over them
cell.addEventListener("mousemove", () => {
//whenever the mouse goes over a cell the callback function in the event listner is executed
//only if the mouse is down then will the the cells be colored
if (mouseDown) {
colorCell(cell, cellColor);
}
});
});
gridContainer.addEventListener("mouseup", () => {
mouseDown = false;
});
row.appendChild(cell);
}
col.appendChild(row);
}
gridContainer.appendChild(col);
};
const createColors = () => {
//for loop to get key in colors object
for (const colorName in colors) {
//create div for each color
const color = document.createElement("div");
color.id = `${colorName}`;
color.classList.add("color");
//styling for each color
color.style.cssText = `width:30px; height:30px; border-radius:50%; background-color:${colors[colorName]}; display:flex; justify-content:center;`;
//when user clicks on color changes global color var to hex color selected
color.addEventListener("click", () => {
cellColor = `${colors[colorName]}`;
});
const colorContainer = document.querySelector(".colors-container");
//apend colors to the color container
colorContainer.appendChild(color);
}
};
function ready(callback) {
// in case the document is already rendered
if (document.readyState != "loading") callback();
else if (document.addEventListener)
document.addEventListener("DOMContentLoaded", callback);
else
document.attachEvent("onreadystatechange", function () {
if (document.readyState == "complete") callback();
});
}
//function only runs once
ready(function () {
//query selector for slider used to control grid size
const slider = document.getElementById("slider");
//need to orginally make the grid once by default as the code below only creates a grid once there is input on the slider
createGrid(1);
// setCellEventListners();
createColors();
//event listner on the slider that executes the call back function inside whenver anyinput occurs on the slider
slider.addEventListener("input", () => {
const sliderValue = document.querySelector(".thumb-value");
sliderValue.innerHTML = `${slider.value}`;
//wipe the current grid so that the new grid does't just get added to the bottom of the current grid. Comment this line out
//and observe what happens when the slider is moved.
wipeGrid();
createGrid(slider.value);
// setCellEventListners();
});
//event listner to reset all cells on grid to having no bg color
const reset = document.querySelector("button");
reset.addEventListener("click", () => {
resetGridColor();
});
//An alternative method to the event listner above
// slider.oninput = function () {
// wipeGrid();
// createGrid(this.value);
// setCellEventListners();
// };
});
* {
box-sizing: border-box;
padding: 0;
margin: 0;
font-family: 'Roboto', sans-serif;
--thumbNumber: '5';
}
.h-100vh {
height: 100vh;
}
.center-me {
display: flex;
align-items: center;
justify-content: center;
}
.grid-container {
border: 0.1px solid black;
height: 400px;
width: 400px;
}
.col {
display: flex;
flex-direction: column;
}
.row {
display: flex;
}
.gap-2em {
gap: 2em;
}
.gap-1em {
gap: 1em;
}
.title {
font-size: 7em;
margin:auto;
}
button {
border:none;
height: 50px;
border-radius: 5px;
background-color: #1682FF;
color:white;
}
button:hover {
background-color: #A0CCFF;
}
.input-label {
font-size: 1.25rem;
}
.colors {
display:flex;
flex-direction: column;
height: 100%;
background-color: rgba(210, 210, 210, 0.1);
border-radius: 10px;
padding: 10px;
justify-content:center;
}
.slider {
-webkit-appearance: none;
width: 100%;
height: 25px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 1;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider:hover {
opacity: 0.7;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
border-radius: 5px;
background: #1682FF;
cursor: pointer;
}
.slider::-webkit-slider-thumb::before {
content: var(--thumbNumber);
}
.range-div {
display:flex;
align-items: center;
gap:0.25em;
}
.colors-container {
width: 270px;
display:flex;
flex-direction: row;
flex-wrap: wrap;
row-gap:30px;
column-gap:20px ;
justify-content:center;
align-items: center;
}
.color-label {
justify-content: space-evenly;
}
.color:hover {
border:none;
outline-offset: 2px;
outline: 2px solid #1682FF;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Etch a Sketch</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&family=Space+Mono&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="center-me h-100vh">
<div class="col gap-2em">
<p class="title">Etch a Sketch</p>
<div class="row gap-2em">
<div class="grid-container"></div>
<div class="col gap-1em">
<div class="slidecontainer col gap-1em">
<label for="slider" class="input-label"> GRID SIZE</label>
<div class="range-div">
<div class="thumb-value input-label">1</div>
<input
name="slider"
type="range"
min="1"
max="100"
value="1"
class="slider"
id="slider"
/>
</div>
</div>
<button class="input-label">RESET</button>
<div class="gap-1em colors">
<p class="input-label row gap-1em color-label">COLORS</p>
<div class="center-me">
<div class="colors-container"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js" async defer></script>
</body>
</html>
My guess as to why this is happening is that the events are bubbling to the grid container since I've put 2 event listeners on it whenever the mouse moves in the grid. To try and fix this I changed the mouse move event on cells to a capture event rather than a bubble, but it didn't seem to work.
The event listeners are set in the createGrid function.
If anyone can help me figure this out it would be much appreciated.