As a programming we type a lot.
But use some random app for typing practice? Nah fam, we are creating our own typing practice game from scratch using vanilla JavaScript.
Video tutorial available on YouTube
Before we start here are the features our game will have:
- Time limit
- Will work with any amount of text
- Highlight text in green if you type correctly
- Highlight text in red if you type incorrectly
- Start game as soon as user stars typing
Okay lets start by creating our html, css and js file.
And then in the html we will have three elements:
- A div where we will show our text
- A div that will work as our progress bar
- A textarea where we can type
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div class="text"></div>
<div class="progress-bar"></div>
<textarea class="input" placeholder="Start Typing"></textarea>
<script src="main.js"></script>
</body>
</html>
Now let's style them in our css
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
background-color: #ffe4c4;
font-family: 'Gill Sans';
}
.text, .input{
display: block;
width: 100%;
border: none;
outline: none;
padding: 1em;
height: calc(50vh - 5px);
font-size: 1.2em;
font-weight: bold;
}
.progress-bar{
height: 10px;
background-color: rgb(0, 255, 0);
}
Now if we open it up in our browser we will see this:
Now we want to show some text in our div. We will do that in JavaScript. Because we want to wrap every character in a span
.
Why? because then we can add a green or red background to the span to indicate if we've typed correct or wrong.
Lets create a function to populate the text in our div
//Any string that you want to use
const str = "Type this as fast as possible."
// Selected necessary elements
const text = document.querySelector(".text")
const input = document.querySelector(".input")
const progressBar = document.querySelector(".progress-bar")
function populateText(str){
// str.split("") will convert our string to characters array
// Then we can loop through them
str.split("").map(letter => {
const span = document.createElement("span")
span.innerText = letter
text.appendChild(span)
})
}
populateText(str)
Now you will see text is displayed and each letter is wrapped by a span
We would need to access those spans later on. So lets store them in a array for easy access.
const str = "Type this as fast as possible."
const text = document.querySelector(".text")
const input = document.querySelector(".input")
const progressBar = document.querySelector(".progress-bar")
const charEls = []
function populateText(str){
str.split("").map(letter => {
const span = document.createElement("span")
span.innerText = letter
text.appendChild(span)
// Adding span in array
charEls.push(span)
})
}
populateText(str)
Great now when we type something in our textarea we want to get the value
const str = "Type this as fast as possible."
const text = document.querySelector(".text")
const input = document.querySelector(".input")
const progressBar = document.querySelector(".progress-bar")
const charEls = []
function populateText(str){
str.split("").map(letter => {
const span = document.createElement("span")
span.innerText = letter
text.appendChild(span)
charEls.push(span)
})
}
populateText(str)
input.addEventListener("keyup", () => {
const val = input.value
console.log(val)
})
Now we are going to loop through all the letters inside our textarea. And for each of the letter we will compare it with the letter of our str
at that index.
If they match that means we typed correct so we can add a correct
class to the span
at that index. Otherwise we will add wrong
input.addEventListener("keyup", () => {
const val = input.value
resetCharEls()
val.split("").map((letter, i) => {
if(letter === str[i]){
charEls[i].classList.add("correct")
}else{
charEls[i].classList.add("wrong")
}
})
})
Let's create those classes in our css
.correct{
background-color: rgb(141, 252, 141);
}
.wrong{
background-color: rgb(250, 132, 132);
}
Now if you type something you will see text is being highlighted accordingly.
Now we need to keep track of the error count(How many letters are not typed correctly).
And we will also check if val.length === str.length
and error count is 0. If so that means we have typed everything correctly.
input.addEventListener("keyup", () => {
const val = input.value
resetCharEls()
let errorCount = 0
val.split("").map((letter, i) => {
if(letter === str[i]){
charEls[i].classList.add("correct")
}else{
charEls[i].classList.add("wrong")
errorCount++
}
})
if(val.length === str.length && errorCount === 0){
alert("Well Done!")
}
})
Great!!! Now lets make the progress bar animate.
We will make the progress bars width go from 100% to 0 in 10 seconds(time is up to you) if it has a class of active. Which we will add in JavaScript.
.progress-bar.active{
animation: 10s animate linear;
}
@keyframes animate{
0%{
width: 100%;
background-color: rgb(0, 255, 0);
}
50%{
width: 50%;
background-color: rgb(238, 255, 0);
}
100%{
width: 0;
background-color: rgb(255, 0, 0);
}
}
And finally when the user starts typing we will add active
class to our progress bar and also start a timer that will alert
Time's up when the time is over.
const str = "Type this as fast as possible."
const text = document.querySelector(".text")
const input = document.querySelector(".input")
const progressBar = document.querySelector(".progress-bar")
// Timer for our game
let timer = null
const charEls = []
function populateText(str){
str.split("").map(letter => {
const span = document.createElement("span")
span.innerText = letter
text.appendChild(span)
charEls.push(span)
})
}
populateText(str)
function resetCharEls(){
charEls.map(charEl => {
charEl.classList.remove("correct")
charEl.classList.remove("wrong")
})
}
input.addEventListener("keyup", () => {
// if timer is null that means the game is not started yet
if(!timer){
progressBar.classList.add("active")
timer = setTimeout(() => {
alert("Time's up!")
}, 10000) // 10000ms = 10s, we have 10s animated duration in css also
}
const val = input.value
resetCharEls()
let errorCount = 0
val.split("").map((letter, i) => {
if(letter === str[i]){
charEls[i].classList.add("correct")
}else{
charEls[i].classList.add("wrong")
errorCount++
}
})
if(val.length === str.length && errorCount === 0){
alert("Well Done!")
// clear the timer when the game is finished
clearTimeout(timer)
}
})
And just like that we have successfully created a typing practice game with nothing but JavaScript.