I'm working on the Odin Project Etch-A-Sketch project using Vanilla Javascript: more details.
Live preview: https://purplepineapple123.github.io/etch-a-sketch/
To start, I've dynamically created a grid of divs in javascript and successfully created buttons (black, psychedelic and user color selection) to change the color of each child div grid element on mouseEnter.
The problem that I'm running into is one of optimization. Every time a user clicks a button, that event is triggered and not forgotten on subsequent button clicks. For example, if you click the "psychedelic-button" 5 times and then hover over the grid, console.log shows that color 5 times for each grid you hover over.
Notice in this screenshot how only 1 grid is highlighted, but the color console.log is layering divs for each button press (I spammed the psychedelic-button for this example.
This issue of layering is really exacerbated on the "chooseColor" button. If the user selects multiple different colors with the color picker, the console.log shows exponential duplicate outputs with each mouseEnter event. This causes massive lag.
Things I've tried:
- Adding and removing classes instead of changing the background color. This worked great except I couldn't figure out a way to add the psychedelic (random JS selected color) to a css class.
- Removing the color changing functions outside of the createGrid() main function. This didn't affect anything because I believe the issue is arising from multiple button clicks.
- Moving the the color changing functions outside of the createGrid() function. Then grabbing the divCol class. Converting that nodeList into an Array. Adding a mouseEnter eventListener forEach element, then changing the color. Same issue with duplications on each button click.
How do I prevent this div stacking and lag from accumulated button clicks?
My code:
HTML
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]> <html class="no-js"> <!--<![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main id="main-content">
<div id="description-container">
<h1 class="animate" id="title">text h1</h1>
<p class="animate" id="desc 1">Text p</p>
</div>
<div id="button-container">
<button id="black-button">Black</button>
<button id="psych-button">Pschedelic</button>
<div>
<input type="color" name="chooseColor" id="chooseColor"
value="#9e0a00">
<label for="chooseColor">Choose A Color</label>
</div>
<!-- <button id="sixteenGrid" >Normal</button>
<button id="fourtyGrid">Large</button> -->
<button id="erase-button">Erase</button>
<button id="reset-button">Reset</button>
</div>
<div id="grid-container"></div>
<!-- <div class="sliderContainer">
<button id="reset-button"></button>
<input type="range" min="1" max="50" value="25" class="slider" id="myRange">
</div> -->
</main>
<script src="main.js" async defer></script>
</body>
</html>
JavaScript
//Generate grid to size of user input
function createGrid(widthHeight){
document.getElementById(`grid-container`).innerHTML = "";
for (let i = 0; i < widthHeight; i++) {
let divRow = document.createElement(`div`);
divRow.classList.add(`divRow`);
document.getElementById(`grid-container`).appendChild(divRow);
for (let y = 0; y < widthHeight; y++){
let divCol = document.createElement(`div`);
divCol.classList.add(`divCol`);
divRow.appendChild(divCol);
//default hover
divCol.addEventListener("mouseenter", function(){
this.classList.add(`black-button`);
this.classList.remove(`psych-button`);
});
//black button
document.getElementById(`black-button`).addEventListener(`click`, function() {
divCol.addEventListener("mouseenter", function(){
this.style.backgroundColor = `black`;
});
});
//psychedlic button selection
document.getElementById(`psych-button`).addEventListener(`click`, function() {
divCol.addEventListener("mouseenter", function(){
this.style.backgroundColor = `#${randomColor()}`;
});
});
//Choose color button
document.getElementById(`chooseColor`).addEventListener(`input`, function() {
divCol.addEventListener("mouseenter", function(){
this.style.backgroundColor = `${chooseColor()}`;
});
}, false);
//eraser button
document.getElementById(`erase-button`).addEventListener(`click`, function() {
divCol.addEventListener("mouseenter", function(){
this.style.backgroundColor = `rgb(156, 149, 136)`;
});
});
}
}
}
createGrid(5);
//return the color choosen by the user
function chooseColor(){
let color = document.getElementById(`chooseColor`).value;
console.log(color);
return color;
}
//returns random colors for psychedleic button
function randomColor() {
let randomColor = Math.floor(Math.random()*16777215).toString(16);
console.log(randomColor);
return randomColor;
}
CSS
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
background-color: aqua;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
button {
padding: 15px;
}
/* begin CSS styling */
#main-content {
display: flex;
width: 100%;
height: 100vh;
flex-direction: column;
}
#description-container {
background-color: orange;
margin-left: auto;
margin-right: auto;
text-align: center;
margin-top: 3%;
}
#button-container {
background-color: green;
margin-left: auto;
margin-right: auto;
display: inline-flex;
margin-top: 1%;
}
#grid-container {
background-color: rgb(156, 149, 136);
display: flex;
flex-direction: column;
margin-left: auto;
margin-right: auto;
width: 600px;
height: 600px;
text-align: center;
margin-top: 1%;
}
.divRow {
display: flex;
width:100%;
height:100%;
margin-top: 1px;
}
.divCol {
flex: auto;
border-right: 1px solid #ccc;
border-bottom:1px solid #ccc;
margin-right: 1px;
}
.reset-color{
background-color: rgb(156, 149, 136);
}
.black-button {
background-color: black;
}
.psych-button {
background-color: red;
}