Tutorial - Shuffle a Deck of Cards in Vue.js

By Hassan Djirdeh (@djirdehh)

What We’re Building 🃏

In this article, we’re going to build a UI that’ll give us the ability to randomly shuffle a deck of cards!

For reference - here’s a complete version of the application:

Note 📝: Be sure to edit/view the application directly on Codepen to view the app in a larger frame!

In the app, users will be able to shuffle a deck of cards in three different speeds. When the deck has been shuffled - users are given the option to reset the deck back to the initial state or continue shuffling.

Context

Since this article assumes little to no familiarity with the Vue library, we’ll be explaining everything thoroughly.

We’ll address 4 main topics:

Preparing the App

The starting point of our application code will be two files - index.html and styles.css.

index.html

The index.html file will represent the root markup page of our application and at the start will look like the following:

In our <head> tag, there are three stylesheet dependencies we’ve included in our application. We’ve introduced Bulma as our applications CSS framework, Font Awesome for icons, and our own styles.css file that’s located in the root of our project.

Our entire application markup will live within the <div id="app"></div> element located in the <body> tag. Within the root id="app" element, there currently exists two child elements, <h1 class="title"></h1> and <div class="deck"></div> that contain some initial markup.

A single <script> element exists at the end of the <body> tag that loads Vue from a Content Delivery Network (CDN).

Note 📝: Using a CDN to load the Vue dependency is one of the simplest and quickest ways to introduce Vue to an application.

styles.css

The styles.css file contains all the custom CSS we’ll need in our application. When we build the markup of our app, we’ll simply declare each element with the appropriate CSS class attributes. This will help maintain our focus on the use of Vue.

At this stage, our app currently only displays the title section and a single static card element.

Note 📝: The HTML section of our Codepen only contains the application UI (i.e. the <div id="app"></div> element) since all dependencies (e.g. Bulma/Vue/JS/etc.) are introduced externally through the Codepen editor.

Creating the card elements

The first thing we’ll aim to achieve is to render a list of card elements for the entire deck of cards. Each card element will be displayed in a simple manner by having a single card be represented with this small amount of markup:

<div class="card">
<span class="card__suit card__suit--top">♣</span>
<span class="card__number">A</span>
<span class="card__suit card__suit--bottom">♣</span>
</div>

With the appropriate styling already established, the markup above will render the card to look like the following:

A standard deck of cards contains four different suits (♣, ♦, ♥, and ♠), with each suit consisting of thirteen different ranks. These ranks include the ace, numbers two to ten, jack, queen, and king. This equates to having 52 unique cards within a deck.

Fun Fact 🎉: This standard deck type is the most widespread and often labelled as the French Standard Deck. Other variations also exist with different deck sizes and different unique cards (e.g. the Joker card).

For the initial non-shuffled state, we’ll look to display all the cards in the deck distinctly separated between the four different suits. Each suit section will display the cards of that suit in ascending order of rank (i.e. from ace to king).

To achieve this, we could statically render each card element for the 52 different cards in the index.html file. This however would result in a lot of duplicate code and be hard to maintain so we’ll instead aim to dynamically render the deck of cards.

The Vue Instance

First and foremost, we’ll create the heart of the Vue application - the Vue instance. The Vue instance accepts an options object which can contain details of the instance such as its template, data, methods, etc.

In a new main.js file, we’ll create a root level Vue instance and declare the DOM element with the id="app" to be where our Vue app is to be mounted upon.

To help dynamically render the non-shuffled deck of cards in our application, we’ll initialize some data values within our instance. We’ll initialize three different properties - rank, suits, and cards. The rank and suits properties will display all the possible ranks and suits that our deck of cards can contain. The cards property will be initialized with an empty array and be dynamically populated when our application first renders.

This makes our Vue instance now look like the following:

We want the cards array to consist of the entire deck of cards in the non-shuffled state. We’ll create a method called displayInitialDeck() that will use the ranks and suits arrays to achieve this. We’ll create this method within the instance’s methods property.

To populate the cards array, we’d essentially need to introduce a card item for every rank in every suit. Each card item we introduce will be an object that contains the properties - id, rank, and suit. These properties will be dynamically bound to the template with which we’ll see shortly.

In the displayInitialDeck() method:

  • We’ll first declare an id variable which will be used for every card object we create. We’ll initialize the id variable with a value of 1.
  • We’ll set the value of cards to an empty array to ensure we’re starting from a blank slate (note: we’ll need this later when we call the displayInitialDeck() method after the deck has already been created).
  • We’ll then specify a nested for loop to be able to loop through every item in ranks for every item in suits. In each iteration, we’ll create a card object and set the appropriate properties.
  • Within the nested loop, we’ll then push the iterated card object to cards and increment the id property.

This makes our displayInitialDeck() method look like the following:

Our displayInitialDeck() method does not need to return this.cards since data variables are reactive by default (when this.cards changes - the view re-renders).

We’ll however need displayInitialDeck() to run when our application gets created at the very beginning. As a result, we’ll run the method in the instance’s created() lifecycle hook which is the method that runs when an instance or component gets created for the very first time.

Now when our application gets created, the cards data property will be populated with all 52 cards in an ordered format. We can now have our HTML template be able to display these cards. Since we aim to render a list of elements based on data source, we’ll use Vue’s native v-for directive.

Rendering a list of cards

In our index.html file, we’ll declare a v-for directive on the markup associated with creating a card element (i.e. the <div class="card"></div> element).

The v-for directive requires the syntax of item in the items array that’s being iterated over. We’ll declare the statement with an alias of card as the item that’s being iterated. For each card item, we’ll bind the values of the card rank and suit on to the element. This now updates our index.html file to the following:

Note 📝: We’re now referencing the newly created main.js file in a <script> tag.

There’s a few things to note here.

  • We’re using the Mustache syntax to bind the card.suit and card.rank values on to the template.
  • To specify the uniqueness of each rendered card item, we’ve bound a key attribute to the value of card.id for each card element. Since we’re using dynamic values, we’re using the shorthand syntax of the v-bind directive to bind our key to card.id.
  • We’re also applying a conditional class binding to the card element with the help of the v-bind directive. Our conditional class states that we’ll add a .black class to the element in which card.suit is ♠ or ♣. If card.suit is either ♦ or ♥, we’ll add a .red class.

Though the conditional class binding we’ve set up above works well, we can make it a little easier to read. Instead of having our card class logic specified on the template alone, we can introduce a data property to help us. We’ll introduce a suitColor object in our instance’s data field like so:

In our rendered card list elements, we can now specify our conditional class binding simply with :class="suitColor[card.suit]":

Now, when we run our application - we’ll be displayed with the entire deck in the initial non shuffled state!

Here’s a running application at this very moment:

The Fisher–Yates Shuffle

shuffleDeck( )

With our deck now being initialized in our app, we can address how we aim to actually shuffle the deck. We’ll establish our own method to achieve this.

Our card elements are being rendered from the items in the cards array. A proper shuffle will entail the reordering of the elements in the array in a random manner. Reordering elements in an array is simple, but to do so in a random manner requires some thought.

Though there are many ways to invoke a shuffle, not all do it in an efficient and random manner. A standard and reliable method often understood to conduct a random shuffle is the Fisher-Yates algorithm (or also known as the Knuth algorithm).

Note 📝: For a very (very) well written post and visualization on the Fisher-Yates shuffle, I highly encourage checking out this article by Mike Bostock.

Though we won’t go into too much detail, we’ll highlight how to construct a Fisher-Yates shuffle. First, we’ll create a method responsible in accomplishing this, called shuffleDeck():

We’ll build out the shuffleDeck() method step by step before displaying how the entire method will be laid out.

The Fisher-Yates algorithm involves looping through and shuffling each element in a data array. To begin, we’ll create a for loop that loops through every item in the cards array. We’ll begin the loop by setting the iterator to this.cards.length — 1, then decrement by one up to the point it hits 0.

shuffleDeck() {
for(let i = this.cards.length - 1; i > 0; i--) {

}
}

The shuffle will first involve picking a random element in the array to shuffle. This can be achieved by determining a random number between 0 and the iterated array length with Math.floor(Math.random() * i). This would randomly give us a index between 0 and the remaining length of the array we want to shuffle. We’ll assign this random number to a variable called randomIndex:

shuffleDeck() {
for(let i = this.cards.length - 1; i > 0; i--) {
let randomIndex = Math.floor(Math.random() * i);
}
}

We’ll then look to get the card item in the array that’s currently being iterated over (i.e. the current card item) and swap that current card item with the item retrieved from the randomIndex. To be able to directly swap between two elements:

  • We’ll set a temp variable to be equal to the value of the current card item.
  • We’ll set the array item for the current index to equal to the array item retrieved for the randomIndex.
  • We’ll set the array item for the randomIndex to be equal to the temp variable, which is the original value of the array item for the current index.

Traditionally, we could achieve this by doing something like:

shuffleDeck() {
for(let i = this.cards.length - 1; i > 0; i--) {
let randomIndex = Math.floor(Math.random() * i);

let temp = this.cards[i];
this.cards[i] = this.cards[randomIndex];
this.cards[randomIndex] = temp;
}
}

When it comes to re-rendering the application UI with the new changes, the above won’t work. This is because Vue cannot pick up the changes made when we directly set the value of an array item or modify its length. To be able to directly update the value of an array data item, we’ll need to use the Vue.set() method.

Vue.set() takes three arguments - 1) the array being updated, 2) the key of the index that is to be updated, and 3) the new value. For example, we can update the first item in the suits array with a 🦆 like so:

Vue.set(this.suits, 0, 🦆);

In our shuffleDeck() method, we’ll use Vue.set() twice to swap the intended elements. This makes the completed shuffleDeck() method look like the following:

The first Vue.set() method sets the value of the card item in the current index to be equal to the item for the randomIndex. The second Vue.set() method sets the value of the array item of the randomIndex to be equal to the temp variable.

And that’s it! We can now create the call to action that will run the shuffleDeck() method when clicked.

Fun Fact 🎉: One of the useful improvements that Vue 3.0 will introduce when launched is the ability to directly update a data array value, without the need to use Vue.set()!
Fun Fact 🎉: Lodash’s _shuffle method uses a version of the Fisher-Yates shuffle.

The Shuffle Button

We’ll introduce the button to invoke a shuffle directly above the deck of cards. In our HTML markup, we’ll place this button element within a <div class="main-buttons"></div> element:

We’ve declared a click event listener on the button element with @click="shuffleDeck" which sets a click event handler to call the shuffleDeck() method when triggered. When we click this Shuffle button, our deck of cards will now shuffle in-place!

@click is the shorthand version of the v-on directive.
🔀

Awesome! With every click, we can notice that every single card in the deck changes. Though the cards are being re-ordered, the re-rendering that occurs with every shuffle happens instantaneously. For us to be able to observe how the cards are changing places, we can use Vue Transitions to see how the elements are being re-ordered.

Here’s our running application at this moment in time:

Transitions

transition-group

Vue provides different ways for us to introduce transitions, such as transitions for a single node, transitions for multiple nodes where only 1 is being rendered at a time, and transitions for a list of elements. Our deck of cards uses the v-for directive to render a list of card items, so we’ll look to apply a List Move Transition to animate the changes in position of each card item.

To perform this, we’ll change the <div class="decks"></div> element to instead be a transition-group element that acts as wrapper to the v-for list (i.e. the deck of cards). As we declare the transition-group, we’ll bind a transition name of shuffleSpeed (with :name="shuffleSpeed") and state that the transition group should be rendered as a div element (with tag="div"):

In our Vue instance, we’ll declare a shuffleSpeed data property and set a string value of shuffleMedium. With this, we’ll be able to control the speed of transitions with which we’ll see shortly.

When the application loads, the transition-group that wraps the deck of cards will have a name attribute value of shuffleMedium. Based on the name of the transition, Vue will automatically recognize if any CSS transitions/animations have been specified. Since we aim to invoke a transition for the movement of items in the list; Vue will look for a specified CSS transition along the lines of shuffleMedium-move (where shuffleMedium is the name given to our transition group).

In the styles.css stylesheet provided in our app, there currently already exists three classes (shuffleSlow-move, shuffleMedium-move, and shuffleFast-move) each responsible in applying a CSS transition. Each of these classes have the same type but a different transition duration.

// Transitions
.shuffleSlow-move {
transition: transform 2s;
}
.shuffleMedium-move {
transition: transform 1s;
}
.shuffleFast-move {
transition: transform 0.5s;
}

With our CSS transitions already added, our card elements will now transition appropriately between locations when a shuffle is invoked!

Fun Fact 🎉: When we click the ‘Shuffle’ button and assuming a completely random shuffle, the odds of having our order of shuffled cards is so unique - it’s likely to have never been seen before in the history of time!
This is because the number of possible permutations is 52! (52 Factorial) - an incredibly large number. Here’s a cool video by Vsauce that explains this some more!

How cool is that! Let’s now look to give the user the choice to dictate how fast or slow they’d want the shuffling to take place.

Transition speed

The transition-group element initially has its name set to a shuffleSpeed data value (which is initialized with shuffleMedium). To allow the user to change the transition speed, we essentially would need to allow the user to change the value of shuffleSpeed.

First thing we’ll do is render three buttons that are labelled Slow, Medium, and Fast. We’ll place these buttons within a <div class="speed-buttons"></div> element, located directly above the ‘Shuffle’ button.

In each introduced button, we’ve specified a conditional class binding to conditionally add a .is-light class depending on the value of shuffleSpeed.is-light is a Bulma class that adds a grey background to the button. We’ve simply set our conditionals to state that the .is-light class should be added to the non-active buttons.

In each button element also exists a click event listener that sets the shuffleSpeed property to the correct intended speed (e.g. clicking on ‘Slow’ sets shuffleSpeed to shuffleSlow). With our different CSS transitions already prepared, we’ll now be able to control the speed of shuffling.

Since each speed button element is very similar to one another, we can reduce the repetition in our HTML markup. To help us, we’ll introduce an array labelled shuffleTypes in our Vue instance that contains each possible type of shuffle:

In our HTML markup, we can use the v-for directive to loop through each possible shuffle type and bind the corresponding information:

This makes our markup a little cleaner and achieves the same result.

We’ve almost completed our app! The last few things we’ll add is a ‘Reset’ button to allow the user to reset a shuffled deck back to its original state, and a counter to count the number of shuffles conducted.

Reset & Counter

Reset Button

For the ability to reset the deck back to its initial state, we’ll introduce a ‘Reset’ button alongside the ‘Shuffle’ button.

In the newly added ‘Reset’ button, we’ve declared v-if="isDeckShuffled" on the element. v-if is a directive that conditionally renders an element based on the truthiness of the statement specified. In this case, we’re stating that the ‘Reset’ button should only be shown when the isDeckShuffled data property is true. We’ll use this data property to only render the ‘Reset’ button when the deck has been shuffled (i.e. when the deck is not in the initial state).

We’ll introduce the isDeckShuffled property in our instance data collection and initialize it with a value of false. At the end of the shuffleDeck() method, we’ll set the value of isDeckShuffled to true to declare that our deck of cards has been shuffled. In the displayInitialDeck() method, we’ll reset the isDeckShuffled property back to false since in this state the deck isn’t shuffled any longer.

We’ll now be able to reset a shuffled deck back to its initial state. The last thing we’ll add is a counter that counts the number of consecutive shuffles the user makes.

Consecutive Shuffles

Let’s introduce a shuffleCount data property responsible in tracking the number of consecutive shuffles. We’ll initialize this data value with 0. When a shuffle is conducted (i.e. shuffleDeck() is called), we’ll increment shuffleCount by 1. When the deck is reset back to its initial state (i.e. displayInitialDeck() is called), we’ll set the value of shuffleCount back to 0.

With these changes, our Vue instance in its entirety will now look like the following:

We’ll now add a <div class="count-section"></div> element at the beginning of our markup. This element will be absolutely positioned to the top right corner of our UI, and will bind the value of shuffleCount by specifying: # of Shuffles: {{ shuffleCount }}.

Our application UI (i.e. the <div id="app"></div> element) in its entirety will be:

Our application is now complete! With the recent changes made, we’ll be able to track the number of consecutive shuffles a user makes and have the ability to reset a shuffled deck back to its initial state.

Here’s the complete application:

Conclusion

Without addressing CSS styling, we walked through building a UI that allows us to shuffle a deck of cards, with the help of the Fisher-Yates shuffle.

The entire app, along with each version of the app as we built it through this tutorial can also be found on Github at the awesome-fullstack-tutorials repo:

I’m always keen on receiving feedback so if there’s anything you’d like to share, please do! You’re more than welcome to leave a comment or message me directly!

If you’ve never used Vue before and are potentially interested in building Vue applications in a more in-depth/robust manner, be sure to check out Fullstack Vue: The Complete Guide to Vue.js!

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

👋🏾

Hi, I’m Hassan. I’m the lead author of Fullstack Vue and a Front End Engineer based out of Toronto, ON. I’m always trying to explain things as simple as possible, so I’ve recently started to blog more about my experiences and give talks on topics I’m passionate about.

You can always connect with me at @djirdehh.