4
\$\begingroup\$

I am a beginner in javascript and I have made this project as a part of The odin project. Feautres:

  1. Draw by hovering the mouse on grid
  2. Erase
  3. Clear the entire sketch at once
  4. Reset the grid size

I have created the grid using only flexbox.

Here is my code:

function createGrid(grid, rowSize, colSize) {
 for (let i = 0; i < rowSize; i++) {
 grid[i] = document.createElement("div");
 grid[i].setAttribute("id", "container");
 let pixels = [];
 for (let j = 0; j < colSize; j++) {
 pixels[j] = document.createElement("div");
 pixels[j].setAttribute("id", "pixel");
 grid[i].appendChild(pixels[j]);
 }
 }
 return grid;
}
function appendArray(pDiv, cDiv) {
 cDiv.forEach((div) => pDiv.appendChild(div));
 return pDiv;
}
(() => {
 const GRID_WIDTH = 16;
 const GRID_HEIGHT = 16;
 const sketchEl = document.querySelector("#sketch");
 const eraseEl = document.querySelector("#erase");
 const buttonsEl = document.querySelector(".buttons");
 let gridCont = document.querySelector("#grid-container");
 let grid = [];
 let state;
 grid = createGrid(grid, GRID_WIDTH, GRID_HEIGHT);
 gridCont = appendArray(gridCont, grid);
 buttonsEl.addEventListener("click", changeState);
 gridCont.addEventListener("mouseover", eventHandler, false);
 function changeState(event) {
 state = event.target.id;
 switch (state) {
 case "reset":
 sketchReset();
 break;
 case "gridSize":
 sizeReset();
 }
 }
 function sketchReset() {
 const pixels = document.querySelectorAll("#pixel");
 pixels.forEach((pxl) =>
 pxl.setAttribute("style", "background-color:'transparent';")
 );
 }
 function sizeReset() {
 let size = parseInt(prompt("Enter grid size (MAX=100) : "));
 if (size <= 100) {
 grid.length = 0;
 grid = createGrid(grid, size, size);
 gridCont.innerHTML = "";
 gridCont = appendArray(gridCont, grid);
 }
 }
 function eventHandler(event) {
 if (event.target.id !== "pixel") {
 return;
 }
 changeColor(event);
 }
 function changeColor(event) {
 if (state === "eraser") {
 event.target.setAttribute("style", "background-color:'transparent';");
 return;
 }
 event.target.setAttribute("style", "background-color:black;");
 }
})();
body {
 display: flex;
 flex-direction: column;
 align-items: center;
 justify-content: center;
}
#header {
 font-size: 2.5em;
}
.flex-container {
 display: flex;
 gap: 2em;
}
.buttons {
 display: flex;
 flex-direction: column;
 justify-content: center;
 gap: 2em;
}
.button {
 font-size: x-large;
}
.grid-container {
 display: flex;
 height: 30em;
 width: 30em;
 border: 1px solid black;
}
.grid-container > div {
 flex: 1;
 display: flex;
 flex-direction: column;
}
.grid-container > div > * {
 flex: 1;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
 <title>Etch-a-Sketch</title>
 <link rel="stylesheet" href="style.css" />
 </head>
 <body>
 <script src="index.js" defer></script>
 <h1 id="header">Etch-a-sketch!</h1>
 <div class="flex-container">
 <div class="buttons">
 <button class="button" id="sketch">Sketch</button>
 <button class="button" id="eraser">Erase</button>
 <button class="button" id="reset">Reset</button>
 <button class="button" id="gridSize">Grid Size</button>
 </div>
 <div id="grid-container" class="grid-container"></div>
 </div>
 </body>
</html>

asked Aug 21, 2022 at 6:46
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I'm going to only focus on the Javascript code in my answer.


function createGrid(grid, rowSize, colSize) {
 for (let i = 0; i < rowSize; i++) {
 grid[i] = document.createElement("div");
 grid[i].setAttribute("id", "container");
 let pixels = [];
 for (let j = 0; j < colSize; j++) {
 pixels[j] = document.createElement("div");
 pixels[j].setAttribute("id", "pixel");
 grid[i].appendChild(pixels[j]);
 }
 }
 return grid;
}

I'm not sure why this takes a grid argument, given that the name is createGrid. Just create the array within the function.

Secondly, ids are supposed to be unique. For a group of similar elements, you should use a class instead. See this SO question for more info.


function appendArray(pDiv, cDiv) {
 cDiv.forEach((div) => pDiv.appendChild(div));
 return pDiv;
}

There already is a function to append multiple children to an element: Element#append. The invocation is a bit different, as you'll need to use spread syntax to pass an array, ie. gridCont.append(...grid).


gridCont.addEventListener("mouseover", eventHandler, false);

eventHandler is an undescriptive name. I would rename it to something like gridMouseover.


 function changeState(event) {
 state = event.target.id;
 switch (state) {
 case "reset":
 sketchReset();
 break;
 case "gridSize":
 sizeReset();
 }
 }

In my opinion, reset and gridSize shouldn't affect the global state, as they are one-time actions. As written, your code works, though restructuring the code will help for future modifications.

 function changeState(event) {
 switch (event.target.id) {
 case "reset":
 sketchReset();
 break;
 case "gridSize":
 sizeReset();
 break;
 case "sketch":
 case "reset":
 state = event.target.id;
 break;
 }
 }

const pixels = document.querySelectorAll(".pixel");

(code modified to align with the createGrid change)

There's no need for querySelectorAll here - just use document.getElementsByClassName("pixel") for better performance and more code clarity.


 function changeColor(event) {
 if (state === "eraser") {
 event.target.setAttribute("style", "background-color:'transparent';");
 return;
 }
 event.target.setAttribute("style", "background-color:black;");
 }

This doesn't need to use the entire event object, just the pixel element that's being hovered, so only pass that as the argument.


Full code:

function createGrid(rowSize, colSize) {
 let grid = [];
 for (let i = 0; i < rowSize; i++) {
 grid[i] = document.createElement("div");
 grid[i].setAttribute("class", "container");
 
 let pixels = [];
 for (let j = 0; j < colSize; j++) {
 pixels[j] = document.createElement("div");
 pixels[j].setAttribute("class", "pixel");
 }
 
 grid[i].append(...pixels);
 }
 return grid;
}
(() => {
 const GRID_WIDTH = 16;
 const GRID_HEIGHT = 16;
 const sketchEl = document.querySelector("#sketch");
 const eraseEl = document.querySelector("#erase");
 const buttonsEl = document.querySelector(".buttons");
 let gridCont = document.querySelector("#grid-container");
 let state;
 let grid = createGrid(GRID_WIDTH, GRID_HEIGHT);
 gridCont.append(...grid);
 buttonsEl.addEventListener("click", changeState);
 gridCont.addEventListener("mouseover", gridMouseover, false);
 function changeState(event) {
 switch (event.target.id) {
 case "reset":
 sketchReset();
 break;
 case "gridSize":
 sizeReset();
 break;
 case "sketch":
 case "reset":
 state = event.target.id;
 break;
 }
 }
 
 function sketchReset() {
 const pixels = document.querySelectorAll(".pixel");
 Array.from(pixels).forEach((pxl) =>
 pxl.setAttribute("style", "background-color:'transparent';")
 );
 }
 
 function sizeReset() {
 let size = parseInt(prompt("Enter grid size (MAX=100) : "));
 if (size <= 100) {
 grid = createGrid(size, size);
 gridCont.innerHTML = "";
 gridCont.append(...grid);
 }
 }
 
 function gridMouseover(event) {
 if (!event.target.classList.contains("pixel")) {
 return;
 }
 changeColor(event.target);
 }
 
 function changeColor(pixel) {
 if (state === "eraser") {
 pixel.setAttribute("style", "background-color:'transparent';");
 return;
 }
 pixel.setAttribute("style", "background-color:black;");
 }
})();
body {
 display: flex;
 flex-direction: column;
 align-items: center;
 justify-content: center;
}
#header {
 font-size: 2.5em;
}
.flex-container {
 display: flex;
 gap: 2em;
}
.buttons {
 display: flex;
 flex-direction: column;
 justify-content: center;
 gap: 2em;
}
.button {
 font-size: x-large;
}
.grid-container {
 display: flex;
 height: 30em;
 width: 30em;
 border: 1px solid black;
}
.grid-container > div {
 flex: 1;
 display: flex;
 flex-direction: column;
}
.grid-container > div > * {
 flex: 1;
}
<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
 <title>Etch-a-Sketch</title>
 <link rel="stylesheet" href="style.css" />
 </head>
 <body>
 <script src="index.js" defer></script>
 <h1 id="header">Etch-a-sketch!</h1>
 <div class="flex-container">
 <div class="buttons">
 <button class="button" id="sketch">Sketch</button>
 <button class="button" id="eraser">Erase</button>
 <button class="button" id="reset">Reset</button>
 <button class="button" id="gridSize">Grid Size</button>
 </div>
 <div id="grid-container" class="grid-container"></div>
 </div>
 </body>
</html>


Some suggestions for future improvements:

  • only draw pixels while the mouse is held - check out the mousedown and mouseup events.
  • display validation errors for sizeReset.
answered Aug 22, 2022 at 23:38
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.