We love a clean bathroom

That’s why I developed a game dedicated to it.

For my 4th phase JS project, I created a Single Page Application with vanilla JavaScript as the front end, and Rails as the back end.

The point of the game is to clean a dirty bathroom under a certain amount of time. Once the time runs out, the game is over, and the user’s scoreboard is rendered. Their score shows up on the scoreboard (no duplicates, and has to be more than 0).

You are also able to view top scores from all of the users that ever scored 700+ points.

For this application, I’ve made 4 fetch requests

  • PATCH to create a user
  • PATCH to create a user’s score
  • GET to render all the top scores
  • GET to render a user’s individual scores

What happens in this game:

  • a User logs in once the page renders ; all the buttons are set up with .preventDefault()
  • EventListeners are attached to DOM elements, with a function that adds points to the user
  • the User has 10 seconds to complete the game, then a function to open the scoreboard modal is fired
  • You close the modal, and the instances for the classes are cleared, as well as .innerHTML of div elements.

Thing’s I’ve learned along the way

Using modals really helped me convey information that I needed without having to refresh the page. A modal is a dialog box/popup window that is displayed on top of the current page.The HTML I grabbed was from W3Schools. I used modals that displayed 3 different types of information at different times each time I triggered a specific event.

A huge thing to be wary of is to make sure that you overwrite, or clear, the innerHTML of the modal whenever you open it, so that you’re not displaying code from the previous time you opened the modal. To do this, you simply set the modal’s class content’s .innerHTML = to a value (or an empty value), instead of .innerHTML += , which freaked out my program and rendered <li> data from another user.

One fancy thing I found was the existence of location.reload() , which I added to an EventListener. This function refreshes the page once it’s run just like your browser’s refresh button would. However, using that function does not meet project requirements since it’s a single page application, so I had to figure out how to reset all my properties manually by clearing values of class instances and variables.

An issue I ran into that took me a while to figure out what was wrong was that after the first User plays the game, and submits their score, the next user who played had points that doubled in value from the original points, and the player after that had points that tripled.

This happened because after the first User completed the game, the dirty bathroom elements were put back into the DOM, and the Event Listener’s function was doubling in value. That EventListener was being fired twice.

Here is the code I used for adding the EventListeners to my image elements:

addEventListeners = (game) => {                           
for(let i = 0; i< arrayOfDirtyItems.length; i++)
let dirtyItem = arrayOfDirtyItems[i][0]
dirtyItem.addEventListener("click", ()=>{
if(gamePlay.user){
game.addPointsAndRemoveElement(arrayOfDirtyItems[i][1],
dirtyItem)
}
})
}
}
addPointsAndRemoveElement = (points, element) => {
if (!this.gameOver){
this.allPoints += points;
element.remove()
document.querySelector("#points").innerHTML= this.allPoints +"
points";
}
}
const arrayOfDirtyItems = [[toiletGerms, 50], [tubDirt, 125], [dirtyLaundry, 75], [dirtyMirror, 50], [dirtPile, 200], [handSplatter1, 100], [handSplatter2, 100]]

An argument of the instance of the game is being passed through, and the function iterates through my dirty items array, which holds all of the image elements, and their point values, then adds an EventListener to each item. Points are being rendered onto that instance of the game [argument].

Once a game starts, the EventListener function is fired, which is great, yeah? But what happens when you rendered all those event listeners onto your DOM, and are about to start a new game? A disaster that’s what.

Well, not horrid, but you are definitely going to double the value of whatever the second element in the nested array is, and render that score to the user.

That’s cheating.

I wanted to find a way to remove an EventListener from the object once the game concludes. You’re in luck! JS has a function for that.

The syntax is just as simple as adding an EL. Your addEventListener is something like target.addEventListener(type, listener) , and the remove EL syntax is almost exactly the same, just with the word remove in it target.removeEventListener(type, listener) .

However, in order for the remove function to work, the listener callback function needs to be exactly the same as the listener callback function in the add function. This logic doesn’t like anonymous functions because every time they are written, it allocated to different memory. At this point, my addEventListener has gotten too complex for me to refractor it out to a way that didn’t make me pull my hair out.

The solution I came to was to create a boolean value on index.js , and wrote it as let hasEventListenerInitializer= false . Once the game is started, there’s a conditional that checks to see if the hasEventListenerInitializer is still false. If so, then you add the event listeners. Once those listeners are triggered, we change the boolean value = true , so we don’t have to re-trigger the listeners again.

The last cool thing I learned while making this project was the use of z-index , which are used in CSS elements.

With z-indexes, you’re able to position items on your DOM that belong to a certain class or ID in any place relative to whatever it’s overlapping. If you wanted to position your elements onto a .wrapper , you would add a CSS definition of position: relative , and any items that would go on top or over that wrapper, you would add a CSS definition of position: absolute . Z-indexes are also given numbers, which correspond to giving it a stack able quality. The if you give the z-index a higher number, it prioritize it in the stack.

After you added z-index numbers, you are now able to manipulate the positioning of whatever element you’re trying to move around. More info on z-indexes.

Boom! This was a really difficult project, the asynchronicity of JS can be difficult to wrap your head around, as well as scopes. My advice is to pay attention to where everything lays in your {} , and try to understand when everything fires.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store