0

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

1 Answer 1

0

Your issue is that you are currently creating new event listeners every time you click one of your buttons. These are never destroyed, so when you spam your psych-button, you create a bunch of event listeners, which all persist and all fire when you enter a square.

A better way is create your event listeners once, and keep a global variable for the color and only update that when you click on one of your buttons.

let color = ""; // new variable to hold color value

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);
            divCol.addEventListener("mouseenter", function () {
                this.style.backgroundColor = color;
            });
        }
    }
}

createGrid(5);

//black button
document.getElementById(`black-button`).addEventListener(`click`, function () {
    color = "black";
});

To get your psych-button working with this new setup, you could store a global boolean value that defines whether a random color should be generated on the fly, and check for that being true in your mouseenter event listener, and generate the color there. Something like this:

let color = "";
let psychColor = false;

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);
            divCol.addEventListener("mouseenter", function () {
                if (psychColor) {
                    color = `#${randomColor()}`;
                }
                this.style.backgroundColor = color;
            });
        }
    }
}

createGrid(5);

//black button
document.getElementById(`black-button`).addEventListener(`click`, function () {
    psychColor = false;
    color = "black";
});

//psychedlic button selection
document.getElementById(`psych-button`).addEventListener(`click`, function () {
    psychColor = true;
});

//returns random colors for psychedleic button
function randomColor() {
    let randomColor = Math.floor(Math.random() * 16777215).toString(16);
    return randomColor;
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.