Pre-planning the Etch-a-Sketch Controls
Based on Toby Parent's article (specification located here).
The Interface Specification
- Button
- A button that sends the user a prompt asking for the number of squares per side for the new grid.
- Removes existing grid and adds a new grid in same total space (container remains static size/width).
- Limit the grid size user inputs to prevent screen freeze/browser crash.
- Hover Effect
- The grid divs should change color when the mouse passes over them, leaving a pixelated-looking trail through the grid, resembling more a light bright toy than etch-a-sketch.
- Hint: Look at the mouseover link for info on the event that occurs when a mouse enters (the boundaries of) an element for more info about hover events as well as others.
- Changing the color of the divs can be done by adding classes or using JS to change the background.
Adding 10 Percent Black Until Completely Black
I'm at the final specification and thinking to init a variable for the RGBa alpha channel which I can then (hopefully) easily increment in my function.
Before I start searching for how to do this, I want to try to conceive of how it will work.
- I need a variable that incrementally adds 10% black to the rgba() property that sets the grid background color.
- The first pass should be 0% black and successive passes should add 10%, so I should initialize alpha as 0 (zero).
Problems So Far…
- It isn't as straightforward as adding an alpha channel to random numbers as this only lightens or darkens the color insteading of adding black.
- Wondering if adding black with an alpha channel of .10 is what would work.
- I think I need an alpha channel to increment the black properly.
- Here's a potentially helpful Stack Overflow post on converting RGB to hexcode color. Not exactly sure how to implement it though.
Thinking About Hover Effects
I only have access to all the gridSquares inside the for
loop. Therefore, in the loop, set all the different classes needed to change the grid colors on mouseover event. Then somehow toggle, add, or remove them when buttons are clicked? Is it as simple as using conditionals?
I also think I have a scoping problem because I can't access ALL gridSquares anywhere other than inside the LOOP. What matters is that I have access to ALL the gridSquares which means working inside the for
loop. If gridSquare doesn't have a 'rainbow' class attribute, add it (and possibly remove or otherwise toggle OFF all other classes)?
Change the hover color to a random RGB value. So for this I need to use the template string (backticks) and interpolate the values into the style property (I think).
A New Random Color For Each Pass Over The gridSquare
I have random background colors for 'mouseover' working on the gridSquares; however, it's not changing successively meaning the color is set once and never changes again after successive mouseovers.
Just off the top of my head, the first couple solutions that come to mind are
- using the
setProperty()
method of the setAttribute()
method- using the
mouseleave
property. - using toggle.
This is the closest I've gotten to a solution that seems to fit my scenario.
None of which I intuitively feel are correct, for some odd reason, but won't let that keep me from investigating further. So I'm feeling a bit defeated tbo. The other idea I'm having is to see if addEventListener options might help, although I don't recall it being so.
I wonder if I use a function to set the red green blue values and as a callback function on the mouse event it would calculate new values each time the event is fired and change the background color on successive passes.
Would using setAttribute, since it updates values that already exist, in a function that calculates and returns the RGB values work?
A test function that calculates the red, green, blue values for the background color and used as an element's callback function on 'mouseenter' does return different values for each pass over the element; therefore, I'm hopeful this is the solution to my problem of getting new background colors on the gridSquares for each pass over them.
And yes, indeed, it was the solution.
See below links on document fragments to learn how to refactor the code. Wondering if I could add all the classes for the control buttons and then use conditionals to implement their use.
Button Prompt
- Create HTML button.
- Select prompt button and add an event listener that prompts user for grid size preference.
- Might be fun to set the initial grid size to a rando number for Vuh-rhy-uh-tay, lol.
- Set Grid Size button
- The
calcGrid()
function sets the initial grid size on page load. - Now I need to add functionality that sets the grid size with the Set Grid Size button if that's been set.
- Seems like
calcGrid()
needs a default squaresPerSize value with conditionals to override it if desired. - Try using ternary logic. I think I need to rename my variables and add one for user choice of grid size.
- Do I attach the button event listener to
calcGrid()
function or do I set the listener as a function? - Not sure how I connect the user choice with the
calcGrid()
function—I just added it to the function and will add conditionals. - If there's a
userChoice
then setsquaresPerSide
to that, otherwise use default. - I don't want the prompt in the
calcGrid()
function because it will always call the prompt on page load. I want the grid to be set on page load and for the prompt to be called when the button is clicked. Then I want theuserChoice
to be saved into the squaresPerSide variable. - I put the conditional resetting
squaresPerSide
touserChoice
in thecalcGrid()
. calcGrid()
is being run on page load and settingsquaresPerSide
.userChoice
isundefined
; so what is that telling me and how can I fix it?- Adding an
if
statement set the default grid size to 16 by 16 if there's no user choice. - I called
calcGrid()
inside the event listener which correctly changes the grid dimensions (confirmed by logging to console) but doesn't update the DOM. - Time to google DOM stuff, I guess.
The set grid size button is working; however, it's creating a new grid directly beneath the first one instead of replacing the original like the spec says to do.
- Possible Fixes
- Get a reference to the original grid selector,
.sketch-container
and somehow append or replace it with the new grid params? - Is there any way to separate the
calcGrid
function into reusable parts, like one part to create and one part to append? I don't think so? Everything seems either necessary to building the grid or wouldn't help by moving to either global scope or to other function. I wouldn't make the.sketchContainer
dynamic because I'm trying to replace it, so IDT having access to a new one makes sense. - I don't see how to rework the
calcGrid()
function and think I need to write something that deals with removing or adding or whatevers like this Node.replaceChild. - I could try to put the
for
loop in a separate function and reuse that for theuserChoice
button but wonder if I'd then have to use more node stuff. I will look at that next. - It was confirmed yesterday on the TOP Discord server that I indeed need to look into using the Node Web API interface in order to get a new grid to overwrite or replace the currently existing one.
Pseudocode For Replacing Default Grid
- I already have a conditional, inside the
calcGrid
function, for the userChoice variable which creates the requested grid but adds it to the end of the existing one instead of replacing it. - I need to call
replaceChild()
or similar to put new grid in existing grid's position and assume this is the last step and therefore goes at the end of thefor
loop. - I will still need to append each gridSquare to the sketchContainer, as that functionality currently works well, so that will stay.
- So after I append all the gridSquares I will run the replaceChild; however, I'll need a new sketchContainer to replace the old one with.
- I think I should initialize the new sketchContainer in the global scope.
- I believe it will also need global scope, and I can always change that later if necessary.
- I think I can find a way to access parent and child nodes in order to replace the default grid with the new user one:
- Node Web API
firstChild
lastChild
parentElement
parentNode
- I have to figure out how I'm going to select the sketchContainer since it's the third child of it's parent. Once selected, I don't want to delete the container since the loop creates and appends the gridSquares to it.
- Problem: I can't seem to select the container's gridSquares in order to delete them since (I believe) there are more than one, meaning it'll be some kind of list or collection which would require a loop. I can certainly loop but am wondering if just creating the container in the loop and then deleting it when there's a new userChoice grid size would be not only easier but more performant. Still a newbie but to me that kinda makes sense.
- Solution: I ended up removing the previous sketchContainer first, after confirming that the user had submitted a valid input. I used the Element Web API remove() method after wrapping the container in a wrapper div element.
Problems So Far
Having trouble with what I'm guessing is a rounding error. My loop is working and the height/width is calculated correctly; however, there are barely perceptible gaps in the grid with a certain number of grid cells and columns. I've tried as many easy/common solutions as I can think of to no avail. I was thinking that perhaps a percentage width and/or height might fix the problem and found this solution that uses flex: 0 0 %
in what appears to be a single loop, so I'll try doing that before turning to the nested loop route.
The Odin server folks suggested this is probably as CSS problem, and I was able to get rid of the gaps inside the grid but there's still a couple nearly imperceptible gaps at the edges now. I'll circle back and address those. For now I have to move on after spending a lot of time nitpicking that detail.
- Example Etch-a-Sketches
- Example Etch-a-Sketches
- Example Etch-a-Sketches
- Example Etch-a-Sketches
- Example Etch-a-Sketches
- Example Etch-a-Sketches
- Example Etch-a-Sketches
- Interesting
canvas
element solution stackoverflow solution. - Changing CSS Properties
- Updating CSS w JS
- Using dynamic Styling
- S.O. events handlers performance thread
- Detecting mouse buttons
- interesting site
- MDN's createDocumentFragment() method
- S.O. on creating elements in a loop
- S.O. create element and set attributes. Only worked with ID for me.