Losing My Constants in a JavaScript Gaming App & Snaking Camel-Case Properties for a Ruby-Based API

David Ryan Morphew
5 min readMay 2, 2021

I just wrote a JavaScript app called the “Mounty Hall Problem App” (which simulates a famous game-show puzzle with a very similar name).

The user is presented with three closed doors. After picking one door, the host opens one of the other two doors to reveal a losing token behind it.

Photo by Jon Sailer on Unsplash

(I used Canadian woodland creatures to represent losing tokens behind the doors. You might want to take them home, but they can be pretty aggressive.)

Now the user is given the choice of staying with their original door pick or switching to the other unopened door.

What should the user do? Stay or switch?

As games are played, the app persists data on a backend Rails API using “fetch” requests. Ruby methods on the backend calculate how often a given user wins when staying with their first pick or switching.

After playing a round of the game, a user can look at the results and the stats for their own plays and for all players, if they so choose (which the frontend application gets back from the API).

You can see how this works in the demo video.

Losing My Constants

After you play a game, you might want to switch users or play again, right? That requires somehow resetting the DOM and what’s present on the page.

Referring to HTML Elements with Constant Variables

This is where I made an interesting discovery. Like many, I like to set assign HTML elements (or nodes) to constant variables so that I can easily refer to them and manipulate them with Javascript. Take, for example, this “<div>”, to which I later add a user name form:

<div class="container white" id="select-user-container" style="display: none"></div>

I can set a variable to access this container:

const userSelectionContainer = document.getElementById('select-user-container');

This pattern works well, unless you overwrite that “<div>,” as I found out the hard way.

Overwriting and Losing Your Referent

My first attempt to reset my game and clear out all of the changes I’d made to the DOM (so that the user could play a new game) resulted in some behavior that baffled me at first.

The container I used to add a new form to the DOM is nested inside of a larger “<div>” container:

<div id="host-prompt-form-div" class="Rtable Rtable--2cols sticky-box">
...
<div class="container white" id="select-user-container"
style="display: none"></div>
</div>

When I reset the game, I accessed this “<div>” and overwrote the “userSelectionContainer”

const hostPromptFormDiv = document.getElementById('host-prompt-form-div');hostPromptFormDiv.innerHTML = 
`...
<div class="container white" id="select-user-container"
style="display: none"></div>`;

And I assumed that the “userSelectionContainer” constant variable would access this “<div>”. The HTML element has the right id. So, you would think that this would still work. But, it doesn’t.

As I investigated with the console, I discovered that I could still manipulate the HTML element that the “userSelectionContainer” variable referred to, but nothing happened on the DOM. I also could access the element on the DOM if I wrote a new variable in the console, like this:

let newUserSelectionContainer = document.getElementById('select-user-container');

But now userSelectionContainer, and newUserSelectionContainer do not refer to the same thing.

By overwriting the HTML of the userSelectionContainer, I lost my constant. The “ghost” HTML element it referred to still showed up in the console, but nowhere on the DOM.

Saving My Constants

There’s an easy fix for this. Don’t overwrite elements that you want to access again with a constant variable. Select and reset those elements themselves:

userSelectionContainer.innerHTML = "";

If you want to overwrite a lot of HTML, but you will want to access some of that overwritten HTML with constants later, consider the following approach.

Example: I wanted to overwrite the images and class Names that were altered during each game round on my door cards.

<div class="container centered" id="current-game-container" style="display: none">
<section class='cards'>
<article class='card' id='door1'>
<h2>DOOR 1</h2>
<img ... >
</article>
<article class="card" id="door2">
<h2>DOOR 2</h2>
<img ... >
</article>
<article class='card' id='door3'>
<h2>DOOR 3</h2>
<img ... >
</article>
</section>
</div>

When I reset the door cards for a new game, I used this:

const currentGameContainer = document.getElementById("current-game-container");

I overwrote all of the innerHTML of the currentGameContainer to reset the game.

Once I overwrote these door cards, I didn’t lose my references to the new cards, since the constant I used to refer to them was built off the currentGameContainer, which was not itself overwritten:

const doorCards = currentGameContainer.getElementsByTagName('article');

This allowed me just the flexibility I wanted.

win-win-win

Each new game, I could overwrite the old set of door cards. This removed the old event listeners and allowed me to reset event listeners on the new door cards . Certain sequences of events in my app depended on resetting these event listeners, so this was very helpful.

I could also keep using the same “doorCards” constant to access the set of door cards I was using each game.

An Alternative

Another way around the issue is to use “let” to allow for variable reassignment:

let userSelectionContainer = document.getElementById('select-user-container');

Then you can reassign the value after overwriting the HTML.

Snaking Camel-Case Properties for a Ruby-based API

Using a Rails API backend with a Javascript frontend, I found my object properties /attributes coming into convention-conflict.

For Javascript, use Camel-Case; for Ruby, Snake-Case.

So, when you use a constructor in Javascript, you take in Snake-Case attributes (from the Rails API) and assign them as Camel-Cased properties of your JavaScript object:

class User {
constructor({user_id, user_name, original_pick, host_reveal,
user_switch, user_win, id}){
this.userId = user_id;
this.userName = user_name;
this.originalPick = original_pick;
this.hostReveal = host_reveal;
this.userSwitch = user_switch;
this.userWin = user_win;
this.id = id;
...
}
...
}

When you send a post request back to the Rails API, you have to convert the properties back from Camel-Case to Snake-Case.

In comes:

Laziness

I decided to let a function do this for me:

class GameApi {
...
static gamePropertyCamelToSnakeCase(property){
const propertyArray = property.split('');

const snakeCaseProperty = propertyArray.map(letter => {
if (letter === letter.toUpperCase() && !letter.match(/[0-9]/)) {
return (letter = `_${letter.toLowerCase()}`);
} else {
return letter;
}
}).join('')
return snakeCaseProperty; }}

Now, for any game, you can iterate through its properties and push the Snake-Cased attributes into an object for use.

Reusable code. Just the right kind of lazy.

--

--

David Ryan Morphew

I’m very excited to start a new career in Software Engineering. I love the languages, frameworks, and libraries I’ve already learned / worked with (Ruby, Rails,