One Approach To Making A Card Game With Firebase Realtime Database Pt II

Myles Tyrer
5 min readOct 30, 2020

This is a continuation of a previous article that showed the basics of an approach I’m trying with the Firebase realtime database to create a webpage that lets me play cribbage with friends and family. It’s my hope that this page will allow me to stay close with people I haven’t been able to see in person for most of 2020, and learning a new skill is always exciting, so let’s go!

SIDE NOTE: The full rules for cribbage can be found here. Be warned, the rules are kind of intricate but they are fairly intuitive. My parents taught me the game when I was young to teach me basic addition and subtraction and now it’s one of my favorite games.

The Approach: Firebase Controls The DOM

The important rule I’m following in this project is that instead of the user controlling the DOM, the user will control the database, and then the database will control the DOM. This will ensure that if you see a “card” element in the DOM, that card represents data from the database available to both players, and is not just a local DOM element on your machine that your opponent will not see.

The Rule In Practice Part I: Updating The Database

When a player clicks or taps a card element in the DOM, it should trigger a change to the database version of that card, then the database triggers the DOM for both players. See a simplified overview in the graphic below.

To put it into practice, in the DOM there is an element to hold each player’s cards before they’re played ( their ‘hand’), and a different element to hold each player’s cards after they’ve been played (the ‘pot’). We want these elements to each have a corresponding database reference that controls their contents. In other words, whenever there is a change in one of these database nodes, we want that change to be mirrored here in our DOM.

First, here is what the HTML looks like.

<!-- the hand elements -->
<div class="player1">
<div class="card Clubs">2</div>
</div>
<div class="player2">
<div class="card Spades">2</div>
</div>
<!-- the pot elements -->
<div class="pots-wrapper">
<div class="player1-pot">
<div class="card Diamonds">J</div>
</div>
<div class="player2-pot">
<div class="card Spades">4</div>
</div>
</div>

And here is what it looks like on the screen.

Let’s say Player 1 clicks the 2 of clubs to play it out of her hand and into the pot. The click event listener attached to the hand DOM element uses a callback function to gather the suit and value of the clicked card and store those values in variables. Note that the DOM hasn’t changed yet.

player1El.addEventListener('click', (e)=>{
//get nearest ancestor card element to the target
let card = e.target.closest('div.card');

//get the suit and value for the card that was clicked
let suit = card.classList[1];
let value = card.innerHTML;
});

Also inside the callback should be a reference to the player’s database hand. We want to see what’s in there and find the card that was clicked.

player1Ref.once('value', (snap)=>{
let hand = snap.val();
//iterate through the cards in the player's hand and find the one that was clicked
for (key in hand) {
// grab the suit and value of the key being checked
let fbSuit = hand[key]['suit'];
let fbValue = hand[key]['value'];
}

As the code iterates through the player’s hand, if a card’s suit and value match those of the card that was clicked, we want the code to play that card into either the crib or the play, depending on what stage of the game we are in. Again, we update the database by calling a firebase method on a database reference, so in this case it will look like this:

//check if the firebase card matches the card clicked
if (fbSuit === suit && fbValue.toString() === value) {
// executes if crib needs to be played
if(gameStage === 'crib') {
crib1Ref.push().set(hand[key]);

// executes if play needs to be played
} else if(gameStage === 'play') {
count1Ref.push().set(hand[key]);
}

Notice again that this event listener callback function does not update the DOM directly in any way. It only makes changes to the firebase database. Now we’ll write the code that will manipulate the DOM.

The Rule In Practice Part II: Controlling The DOM Through Firebase

Now that clicking a card updates the database, the database needs to update the DOM to reflect that change. Firebase has an event that can fire any time there is a change to any part of the database:

player2Ref.on('value', (snap) => {
console.log(snap.value);
})

In the above callback, snap.value is an object which is a snapshot of the data in Player 2’s database node at the moment the event fired, including the new data. This means any time any of the data in Player 2’s database changes (like in the click event from above) the entire data node will be logged to the console. All we need to do is manipulate the DOM from inside this callback and we’ll have an automatic DOM update function!

player2Ref.on('value', (snap)=>{
let hand = snap.val();
//reset player hand element
player2El.innerHTML='';
// custom DOM update function
getFBHand(player2El, hand);
})

In the above callback, hand represents the cards in the player’s hand. Then the code resets the element’s contents to an empty string, so that the entire contents of the player’s hand can be written in. getFBHand(player2El, hand) is a custom function that takes two parameters. The first parameter is the element in the DOM where the database data should be represented as cards. The second argument is the firebase data, as a javascript object. Here is what’s inside getFBHand:

function getFBHand(handEl, hand){
for(key in hand){
// console.log(key);
let suit = hand[key].suit;
let value = hand[key].value;
let cardEl = document.createElement('div');
cardEl.classList.add('card');
cardEl.classList.add(suit);
cardEl.innerHTML = value;
handEl.appendChild(cardEl);
}

The function will iterate through the cards in hand and use the data in each one to generate a card div, then append that div to the element passed as handEl.

All player’s nodes in the database will have this ‘value’ event listener and the getFBHand callback attached to them, so any change in the database is reflected in the DOM.

That concludes the basic overview of my approach to manipulating the DOM through the Firebase database.

Right now, the player’s hands are still not hidden from each other, but that will come later.

Next Time: Server Logic

I am also working on a server that will allow people to share a unique link with a friend so they can play a private game together. I will start to cover that approach next time.

Thanks for reading!

--

--

Myles Tyrer

I’m a web developer focusing on interactive projects using React and Nodejs.