addEventListener for type=button only works once

Viewed 525

I'm working on an assignment, where the user can draw inside an n*n grid. The tasks are as following:

  • Create a 16x16 grid as default grid upon page-load
  • Implement a mouseover-EventListener for the grid, that changes the background-color of cells to black
  • Add a reset button, that asks the user how big the new grid should be
  • Add 2 buttons that set the mouseover-EventListener (default black) to a different color. First button changes the color to yellow, second button to gray.

My problem: I'm stuck with the last task. The 2 buttons work just fine. But they can be called only once. If I select yellow - it draws yellow. If I then select gray - it draws gray. After that, the button-clicks don't do anything anymore, but I can still draw in gray.

That's what I have done so far: First, I querySelected the button-type and added an EventListener "click".

Second, in the colourHover function, a mouseover EventListener gets invoked, depending on the button's id.

Third, the EventListener-mouseover gets defined in the respective functions (grayColour(event), yellowColour(event)).

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body id="start">
<div class="container" id="grid">
</div>
<div><button type="button" class="button" id="gray">Gray Colour</button></div>
<div><button type="button" class="button" id="yellow">Yellow Colour</button></div>
<div><button type="button" id="button">Resize your grid manually.</button></div>

<script>
"use strict";

let container = document.getElementById("grid");
let n;

// creating a grid with number-input from user.
let makeGrid = function makeGrid(input) {
    let c;
    
    for (c = 0; c < (input * input); c++) {
        let cell = document.createElement("div");
        container.appendChild(cell).className = "squares";
        
    };
    container.style.gridTemplateColumns = `repeat(${input}, auto)`;
    container.style.gridTemplateRows = `repeat(${input}, auto)`;
};

// function gets exectuted, only if a number and below 101
let numberCheck = function numberCheck() {
    
    if ((n % n) === 0 && n <= 100) {
    makeGrid(n);
};

    while ((n % n) !== 0) {
        alert("Please enter a number.");
        n = prompt("Choose again.");
        if ((n % n) === 0 && n <= 100) {
            makeGrid(n);
        }
    };

    while ((n % n) === 0 && n > 100) {
        alert("The number is too high.");
        n = prompt("Choose again.");
        if ((n % n) === 0 && n <= 100) {
            makeGrid(n);
        }
        else if ((n % n) !== 0) {
            alert("Please enter a number");
            n = prompt("Choose again");
                if ((n % n) === 0 && n <= 100) {
                    makeGrid(n);
                }
        }
    };
    
};

// default grid on first pageload
makeGrid(16);

// this function sets the hovered items colour to black on default / first pageload
let blackColor = function blackColor(event) {
    let colour = event.target;
    if (colour.className === "container") {
        return;
    }
    else if (colour.className === "squares") {
        colour.style.backgroundColor = "black";
    };
};

// addEventListener for blackColor function
let hoverItems = document.querySelectorAll(".container");
    hoverItems.forEach(element => { element.addEventListener("mouseover", blackColor)
});



// function to manual selecting a grid-size. Beforehand, old grid gets deleted
let removeElements = function removeElements(event) {
    let elements = document.getElementsByClassName("squares");
    while (elements.length > 0) {
        elements[0].parentNode.removeChild(elements[0]);
    };
    n = prompt("Choose the size of your grid");
    numberCheck(n);
};

// button for selecting a grid-size manually
let button = document.querySelector("#button");
button.addEventListener("click", removeElements);


// this function sets the background-color of the cells on mouseover to gray
let grayColour = function grayColour(event) {
        let colour = event.target;
        if (colour.className === "container") {
        return;
    }
    else if (colour.className === "squares") {
        colour.style.backgroundColor = "gray"

    }
}
// this function sets the background-color of the cells on mouseover to yellow
let yellowColour = function yellowColour(event) {
    let colour = event.target;
    if (colour.className === "container") {
        return;
    }
    else if (colour.className === "squares") {
    colour.style.backgroundColor = "yellow";

}
}


let colourHover = function colourHover(event) {
    let hover = document.querySelectorAll(".container");
    switch (event.target.id) {
        case "gray":
            //hover.forEach(element => { element.removeEventListener("mouseover", yellowColour)
            //});
            //hover.forEach(element => { element.removeEventListener("mouseover", grayColour)
            //});
            hover.forEach(element => { element.addEventListener("mouseover", grayColour)
        });
        break;
        case "yellow":
        //hover.forEach(element => { element.removeEventListener("mouseover", yellowColour)
            //});
            //hover.forEach(element => { element.removeEventListener("mouseover", grayColour)
            //});
        hover.forEach(element => { element.addEventListener("mouseover", yellowColour)
        });
        break;
};
};

let colourBtn = document.querySelectorAll(".button");
colourBtn.forEach(element => { element.addEventListener("click", colourHover)
});
</script>



</body>
</html>

I appreciate any hint in the right direction. Really stuck right now.

1 Answers

The simplest way is to use CSS classes for styling the cells, and change the class of the table body in a button click handler. The state of the "widget" should be stored in a JS variable, that way you can avoid unnecessary DOM traversing on each click of the buttons. Something like this:

const buttons = document.querySelectorAll('button'),
    table = document.querySelector('.hovered-colors');
// The state of the widget
let currentColor = 'black';

buttons.forEach(button => {
  button.addEventListener('click', e => {
    const newColor = e.target.getAttribute('data-color');
    table.classList.remove(currentColor);
    table.classList.add(newColor);
    currentColor = newColor;
  });
});
.black td:hover {
  color: white;
  background-color: black;
}

.gray td:hover {
  color: black;  
  background-color: gray;
}

.yellow td:hover {
  color: black;  
  background-color: yellow;
}
<table>
  <tbody class="hovered-colors black">
    <tr><td>R1C1</td><td>R1C2</td></tr>
    <tr><td>R2C1</td><td>R2C2</td></tr>
    <tr><td>R3C1</td><td>R3C2</td></tr>
  </tbody>
</table>

<button data-color="gray">Gray</button>
<button data-color="yellow">Yellow</button>

Related