These days it seems to be a lot of progress for the web. There is almost numerous full choices to choose libraries and framework.
You will probably need to enforce a blood phase, version control, and a deployed pipeline. Before you write a line of code. What do you think about entertainment advice? Let’s take a step back and remind yourself about how a shining extra can be, how can the sovereignty and powerful modern JavaScript and CSS be.
Interested? Then come with me, only on a browser -based journey using Vanilla JS and CSS.
Idea
We will be making a flag -assessment game. The player has been presented with a list of flags and multiple selection style responses.
Step 1. The infrastructure
First, we will need a list of countries and their related flags. Thankfully, we can use the power of emojis to show flags, which means that we do not need to create ourselves or even worse, they have to create themselves. I have developed it in Json form.
On its easiest thing the interface will show a flag emoji and five buttons:
A dash of CSS using the grid to center everything and relative size so it shows it well from the smallest screen to the largest monitor.
Now hold a copy of ours Starter ShamWe will build on it all the way
Lead
File structure for our project seems to be:
step1.html
step2.html
js/
data.json
helpers/
css/
i/
At the end of each section, it will have a link to our code in the current state.
Step 2. A simple prototype type
Let’s cracking. First, we need to occupy our Data.json File
async function loadCountries(file) {
try {
const response = await fetch(file);
return await response.json();
} catch (error) {
throw new Error(error);
}
}
loadCountries('./js/data.json')
.then((data) => {
startGame(data.countries)
});
Now that we have data, we can start the game. The following code has been openly commented on. It takes a few minutes to read and get a handle on what’s going on.
function startGame(countries) {
shuffle(countries);
let answer = countries.shift();
let selected = shuffle((answer, ...countries.slice(0, 4)));
document.querySelector('h2.flag').innerText = answer.flag;
document.querySelectorAll('.suggestions button')
.forEach((button, index) => {
const countryName = selected(index).name;
button.innerText = countryName;
button.dataset.correct = (countryName === answer.name);
button.onclick = checkAnswer;
})
}
And some logic to check the answer:
function checkAnswer(e) {
const button = e.target;
if (button.dataset.correct === 'true') {
button.classList.add('correct');
alert('Correct! Well done!');
} else {
button.classList.add('wrong');
alert('Wrong answer try again');
}
}
You have probably seen that our startGame
The function is called Shuffle Function. Here is a simple process of Fisher-Ets algorithm:
function shuffle(array) {
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array(m);
array(m) = array(i);
array(i) = t;
}
return array;
}
Step 3. A little class
A little housing time. Modern libraries and frames often force some conventions that help implement structures on apps. As matters start to grow, it makes sense and placing all code in a file soon makes it dirty.
Let’s take advantage of the strength of modules to keep your code, error, modular. Update your HTML file by changing the inline script with it:
<script type="module" src="./js/step3.js">script>
Now, JS/Step 3. In J. We can load our helpers:
import loadCountries from "./helpers/loadCountries.js";
import shuffle from "./helpers/shuffle.js";
Make sure to transfer the shuffle and load county functions to their relevant files.
Note: For example, we will also import our data as a module, but unfortunately, Firefox does not support import claims.
You will also need to start each function with an export default. For example:
export default function shuffle(array) {
...
We will also wrap our game logic in the game class. This helps maintain the integrity of the data and helps keep the code more secure and maintained. It takes a minute to read through code comments.
loadCountries('js/data.json')
.then((data) => {
const countries = data.countries;
const game = new Game(countries);
game.start();
});
class Game {
constructor(countries) {
this.masterCountries = countries;
this.DOM = {
flag: document.querySelector('h2.flag'),
answerButtons: document.querySelectorAll('.suggestions button')
}
this.DOM.answerButtons.forEach((button) => {
button.onclick = (e) => {
this.checkAnswer(e.target);
}
})
}
start() {
this.countries = shuffle((...this.masterCountries));
const answer = this.countries.shift();
const selected = shuffle((answer, ...this.countries.slice(0, 4)));
this.DOM.flag.innerText = answer.flag;
selected.forEach((country, index) => {
const button = this.DOM.answerButtons(index);
button.classList.remove('correct', 'wrong');
button.innerText = country.name;
button.dataset.correct = country.name === answer.name;
});
}
checkAnswer(button) {
const correct = button.dataset.correct === 'true';
if (correct) {
button.classList.add('correct');
alert('Correct! Well done!');
this.start();
} else {
button.classList.add('wrong');
alert('Wrong answer try again');
}
}
}
Step 4. Scoring and Game Over -screen
Let’s update the game converter to handle multiple rounds:
class Game {
constructor(countries, numTurns = 3) {
// number of turns in a game
this.numTurns = numTurns;
...
Our Dome will need to be updated so that we can handle the game more than the state, add the replay button and display the score.
<main>
<div class="score">0div>
<section class="play">
...
section>
<section class="gameover hide">
<h2>Game Overh2>
<p>You scored:
<span class="result">
span>
p>
<button class="replay">Play againbutton>
section>
main>
We only hide the game unless it is needed.
Now, add references to these new domestic elements to our game converter:
this.DOM = {
score: document.querySelector('.score'),
play: document.querySelector('.play'),
gameover: document.querySelector('.gameover'),
result: document.querySelector('.result'),
flag: document.querySelector('h2.flag'),
answerButtons: document.querySelectorAll('.suggestions button'),
replayButtons: document.querySelectorAll('button.replay'),
}
We will also clean our game startup procedures, and transfer the logic to the countries to display a separate method. This will help keep things clean and manageable.
start() {
this.countries = shuffle((...this.masterCountries));
this.score = 0;
this.turn = 0;
this.updateScore();
this.showCountries();
}
showCountries() {
// get our answer
const answer = this.countries.shift();
// pick 4 more countries, merge our answer and shuffle
const selected = shuffle((answer, ...this.countries.slice(0, 4)));
// update the DOM, starting with the flag
this.DOM.flag.innerText = answer.flag;
// update each button with a country name
selected.forEach((country, index) => {
const button = this.DOM.answerButtons(index);
// remove any classes from previous turn
button.classList.remove('correct', 'wrong');
button.innerText = country.name;
button.dataset.correct = country.name === answer.name;
});
}
nextTurn() {
const wrongAnswers = document.querySelectorAll('button.wrong')
.length;
this.turn += 1;
if (wrongAnswers === 0) {
this.score += 1;
this.updateScore();
}
if (this.turn === this.numTurns) {
this.gameOver();
} else {
this.showCountries();
}
}
updateScore() {
this.DOM.score.innerText = this.score;
}
gameOver() {
this.DOM.play.classList.add('hide');
this.DOM.gameover.classList.remove('hide');
this.DOM.result.innerText = `${this.score} out of ${this.numTurns}`;
}
At the bottom of the game converter’s procedure, we will
Listen to the clicks on the replay button. I
One -click event, we re -start by calling the start method.
this.DOM.replayButtons.forEach((button) => {
button.onclick = (e) => {
this.start();
}
});
Finally, add a dash of styles to the letters, place the score and position
Add our .Hid class to togel the game as needed.
button.correct { background: darkgreen; color: #fff; }
button.wrong { background: darkred; color: #fff; }
.score { position: absolute; top: 1rem; left: 50%; font-size: 2rem; }
.hide { display: none; }
Development! Now we have a very easy game.
However, it is a bit blend. Let’s address it
In the next step
Step 5. Bring billing!
CSS dynamic images are a very easy and successful way
Bring stable elements and interfaces into life.
Frames of
Allow us to explain the key frames of animation layout with changing
CSS Properties. Consider this to slide our country list on the screen and on off:
.slide-off { animation: 0.75s slide-off ease-out forwards; animation-delay: 1s;}
.slide-on { animation: 0.75s slide-on ease-in; }
@keyframes slide-off {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(50vw); }
}
@keyframes slide-on {
from { opacity: 0; transform: translateX(-50vw); }
to { opacity: 1; transform: translateX(0); }
}
When we start the game, we can apply the sliding effect …
start() {
// reset dom elements
this.DOM.gameover.classList.add('hide');
this.DOM.play.classList.remove('hide');
this.DOM.play.classList.add('slide-on');
...
}
… And in the next time the procedure
nextTurn() {
...
if (this.turn === this.numTurns) {
this.gameOver();
} else {
this.DOM.play.classList.remove('slide-on');
this.DOM.play.classList.add('slide-off');
}
}
After checking the answer, we also need to call the Next turn method. Update the Checkensor Procedure to achieve this:
checkAnswer(button) {
const correct = button.dataset.correct === 'true';
if (correct) {
button.classList.add('correct');
this.nextTurn();
} else {
button.classList.add('wrong');
}
}
Once the slide off animation is over, we need to slide it back and update the country’s list. We can set a timeout based on the length of the animation, and on the basis of performing this logic. Thanks, an easy way to use animation event is:
// listen to animation end events
// in the case of .slide-on, we change the card,
// then move it back on screen
this.DOM.play.addEventListener('animationend', (e) => {
const targetClass = e.target.classList;
if (targetClass.contains('slide-off')) {
this.showCountries();
targetClass.remove('slide-off', 'no-delay');
targetClass.add('slide-on');
}
});
Step 6. Last touch
Wouldn’t it be good to add the title screen? In this way the user is given a little context and is not thrown straight into the game.
Our markup will look like this:
<div class="score hide">0div>
<section class="intro fade-in">
<h1>
Guess the flag
h1>
<p class="guess">🌍p>
<p>How many can you recognize?p>
<button class="replay">Startbutton>
section>
<section class="play hide">
...
Let’s hook the interview screen in the game.
We will need to add a reference to the Dom Elements:
this.DOM = {
intro: document.querySelector('.intro'),
....
Then hide it while starting the game:
start() {
this.DOM.intro.classList.add('hide');
this.DOM.score.classList.remove('hide');
...
Also, don’t forget to add new style:
section.intro p { margin-bottom: 2rem; }
section.intro p.guess { font-size: 8rem; }
.fade-in { opacity: 0; animation: 1s fade-in ease-out forwards; }
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
Wouldn’t it be good for the player to be ranked on the basis of their score? It is extremely easy to implement. As can be seen, in the latest gameover method:
const ratings = ('💩','🤣','😴','🤪','👎','😓','😅','😃','🤓','🔥','⭐');
const percentage = (this.score / this.numTurns) * 100;
const rating = Math.ceil(percentage / ratings.length);
this.DOM.play.classList.add('hide');
this.DOM.gameover.classList.remove('hide');
this.DOM.gameover.classList.add('fade-in');
this.DOM.result.innerHTML = `
${this.score} out of ${this.numTurns}
Your rating: ${this.ratings(rating)}
`;
}
One last last touch; An excellent animation when the player makes a proper guess. To achieve this effect, we can once again turn to CSS dynamic images.
button::before { content: ' '; background: url(../i/star.svg); height: 32px; width: 32px; position: absolute; bottom: -2rem; left: -1rem; opacity: 0; }
button::after { content: ' '; background: url(../i/star.svg); height: 32px; width: 32px; position: absolute; bottom: -2rem; right: -2rem; opacity: 0; }
button { position: relative; }
button.correct::before { animation: sparkle .5s ease-out forwards; }
button.correct::after { animation: sparkle2 .75s ease-out forwards; }
@keyframes sparkle {
from { opacity: 0; bottom: -2rem; scale: 0.5 }
to { opacity: 0.5; bottom: 1rem; scale: 0.8; left: -2rem; transform: rotate(90deg); }
}
@keyframes sparkle2 {
from { opacity: 0; bottom: -2rem; scale: 0.2}
to { opacity: 0.7; bottom: -1rem; scale: 1; right: -3rem; transform: rotate(-45deg); }
}
We use the background icon (stars.SVG): Before and :: Tamkad elements, but we keep it hidden by fading on 0. Then when the button name of the button is correct, it is activated by emphasizing the shining animation. Remember, when the correct answer is selected, we already put this class on the button.
Wrap and some extra ideas
In less than 200 lines of JavaScript (freely comment), we have completely
Working, mobile friendly games. And not a single dependent or library in sight!
Of course, here are endless features and improvements that we can add to our game.
If you like that here a challenge is some ideas:
- Add basic sound effects for correct and wrong answers.
- Make the game offline using the web workers
- Store stats such as the number of dramas, the overall classification in local store, and the display
- Add a way to share your score and challenge friends on social media.
Unlock Your Business Potential with Stan Jackowski Designs
At Stan Jackowski Designs, we bring your ideas to life with cutting-edge creativity and innovation. Whether you need a customized website, professional digital marketing strategies, or expert SEO services, we’ve got you covered! Our team ensures your business, ministry, or brand stands out with high-performing solutions tailored to your needs.
🚀 What We Offer:
- Web Development – High-converting, responsive, and optimized websites
- Stunning Design & UI/UX – Eye-catching visuals that enhance engagement
- Digital Marketing – Creative campaigns to boost your brand presence
- SEO Optimization – Increase visibility, traffic, and search rankings
- Ongoing Support – 24/7 assistance to keep your website running smoothly
🔹 Take your business to the next level! Explore our outstanding services today:
Stan Jackowski Services
📍 Located: South of Chicago
📞 Contact Us: https://www.stanjackowski.com/contact
💡 Bonus: If you’re a ministry, church, or non-profit organization, we offer specialized solutions, including website setup, training, and consultation to empower your online presence. Book a FREE 1-hour consultation with Rev. Stanley F. Jackowski today!
🔥 Looking for a done-for-you autoblog website? We specialize in creating money-making autoblog websites that generate passive income on autopilot. Let us handle the technical details while you focus on growth!
📩 Let’s Build Something Amazing Together! Contact us now to get started.