Typing Speed Test Game using HTML, CSS and JavaScript

0
86

Typing on a computer involves using a keyboard to input text or commands into a digital interface. The keyboard typically contains alphanumeric keys, as well as various function keys and special keys for specific purposes. When you press a key, it sends an electronic signal to the computer, which then interprets it and displays the corresponding character on the screen.

Typing speed refers to how quickly and accurately you can input text using a keyboard. It’s usually measured in words per minute (WPM). The average typing speed for adults is around 40 words per minute, but proficient typists can reach speeds of 60 words per minute or more.

Follow this video for complete guidance :

Improving typing speed involves developing both speed and accuracy. Practicing regularly can help you become more familiar with the keyboard layout and improve your muscle memory, allowing you to type more quickly without looking at the keys. Additionally, learning touch typing, where you type without looking at the keyboard, can greatly increase your speed over time.

There are many online resources and typing programs available to help you improve your typing speed and accuracy. These often include exercises and drills designed to reinforce typing skills and increase speed. With practice and dedication, you can gradually increase your typing speed and become a more efficient typist.

Typing Speed Test Source Code

index.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">  
        <title>Typing Speed Test App</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <!-- CSS -->
        <link rel="stylesheet" href="./style.css">
    </head>
    <body>
        <div class="wrapper">
            <input type="text" class="input-field">
            <div class="content-box">
                <h1 style="color: #333;">Typing Speed Test App</h1> 
                <div class="content">
                    <ul class="result-details">
                        <li class="time">
                            <p style="color: #333;">Time Left:</p> 
                            <span><b>60</b>s</span>
                        </li>
                        <li class="mistake">
                            <p style="color: #333;">Mistakes:</p>
                            <span>0</span>
                        </li>
                        <li class="wpm">
                            <p style="color: #333;">WPM:</p>
                            <span>0</span>
                        </li>
                        <li class="cpm">
                            <p style="color: #333;">CPM:</p> 
                            <span>0</span>
                        </li>
                    </ul>
                    <button style="background: #4CAF50;">Try Again</button>
                </div>
                <div class="typing-text">
                    <p></p>
                </div>
            </div>
        </div>

        <!-- Javascript -->
        <script src="./js/paragraphs.js"></script>
        <script src="./js/script.js"></script>
    </body>
</html>

style.css

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: sans-serif;
}

body {
    display: flex;
    padding: 0 10px;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: #83c7b1;
}

::selection {
    color: #fff;
    background: #4CAF50;
}

.wrapper {
    text-align: center;
    width: 770px;
    padding: 35px;
    background: #F4F4F4;
    border-radius: 20px;
    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}

.wrapper .input-field {
    opacity: 0;
    z-index: -999;
    position: absolute;
}

.wrapper .content-box {
    padding: 20px;
    border-radius: 20px;
    border: 1px solid #E0E0E0;
}

.content-box .typing-text {
    overflow: hidden;
    max-height: 256px;
}

.typing-text::-webkit-scrollbar {
    width: 0;
}

.typing-text p {
    font-size: 18px;
    text-align: justify;
    letter-spacing: 1px;
    word-break: break-all;
    color: #333;
}

.typing-text p span {
    position: relative;
}

.typing-text p span.correct {
    color: #4CAF50;
}

.typing-text p span.incorrect {
    color: #FF5722;
}

.typing-text p span.active {
    color: #3498DB;
}

.typing-text p span.active::before {
    position: absolute;
    content: "";
    height: 2px;
    width: 100%;
    bottom: 0;
    left: 0;
    opacity: 0;
    border-radius: 5px;
    background: #3498DB;
    animation: blink 1s ease-in-out infinite;
}

@keyframes blink {
    50% {
        opacity: 1;
    }
}

.content-box .content {
    margin-top: 17px;
    display: flex;
    padding: 12px 0;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    border-top: 1px solid #E0E0E0;
    border-bottom: 1px solid #e0e0e0;
    margin-bottom: 20px;
}

.content button {
    outline: none;
    border: none;
    width: 105px;
    color: #fff;
    padding: 8px 0;
    font-size: 16px;
    cursor: pointer;
    border-radius: 5px;
    background: #3498DB;
    transition: transform 0.3s ease;
}

.content button:active {
    transform: scale(0.97);
}

.content .result-details {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    width: calc(100% - 140px);
    justify-content: space-between;
}

.result-details li {
    display: flex;
    height: 20px;
    list-style: none;
    position: relative;
    align-items: center;
}

.result-details li:not(:first-child) {
    padding-left: 22px;
    border-left: 1px solid #E0E0E0;
}

.result-details li p {
    font-size: 18px;
}

.result-details li span {
    display: block;
    font-size: 18px;
    margin-left: 10px;
}

li span b {
    font-weight: 500;
}

li:not(:first-child) span {
    font-weight: 500;
}

@media (max-width: 745px) {
    .wrapper {
        padding: 20px;
    }

    .content-box {
        text-align: center;
    }

    .content-box .content {
        padding: 20px 0;
    }

    .content-box .typing-text {
        max-height: 100%;
    }

    .typing-text p {
        font-size: 16px;
        text-align: left;
    }

    .content button {
        width: 100%;
        font-size: 15px;
        padding: 10px 0;
        margin-top: 20px;
    }

    .content .result-details {
        width: 100%;
    }

    .result-details li:not(:first-child) {
        border-left: 0;
        padding: 0;
    }

    .result-details li p,
    .result-details li span {
        font-size: 16px;
    }
}

@media (max-width: 518px) {
    .wrapper .content-box {
        padding: 10px 15px 0;
    }

    .typing-text p {
        font-size: 16px;
    }

    .result-details li {
        margin-bottom: 10px;
    }

    .content button {
        margin-top: 10px;
    }
}

script.js

const typingText = document.querySelector(".typing-text p"),
inpField = document.querySelector(".wrapper .input-field"),
tryAgainBtn = document.querySelector(".content button"),
timeTag = document.querySelector(".time span b"),
mistakeTag = document.querySelector(".mistake span"),
wpmTag = document.querySelector(".wpm span"),
cpmTag = document.querySelector(".cpm span");

let timer,
maxTime = 60,
timeLeft = maxTime,
charIndex = mistakes = isTyping = 0;

function loadParagraph() {
    const ranIndex = Math.floor(Math.random() * paragraphs.length);
    typingText.innerHTML = "";
    paragraphs[ranIndex].split("").forEach(char => {
        let span = `<span>${char}</span>`
        typingText.innerHTML += span;
    });
    typingText.querySelectorAll("span")[0].classList.add("active");
    document.addEventListener("keydown", () => inpField.focus());
    typingText.addEventListener("click", () => inpField.focus());
}

function initTyping() {
    let characters = typingText.querySelectorAll("span");
    let typedChar = inpField.value.split("")[charIndex];
    if(charIndex < characters.length - 1 && timeLeft > 0) {
        if(!isTyping) {
            timer = setInterval(initTimer, 1000);
            isTyping = true;
        }
        if(typedChar == null) {
            if(charIndex > 0) {
                charIndex--;
                if(characters[charIndex].classList.contains("incorrect")) {
                    mistakes--;
                }
                characters[charIndex].classList.remove("correct", "incorrect");
            }
        } else {
            if(characters[charIndex].innerText == typedChar) {
                characters[charIndex].classList.add("correct");
            } else {
                mistakes++;
                characters[charIndex].classList.add("incorrect");
            }
            charIndex++;
        }
        characters.forEach(span => span.classList.remove("active"));
        characters[charIndex].classList.add("active");

        let wpm = Math.round(((charIndex - mistakes)  / 5) / (maxTime - timeLeft) * 60);
        wpm = wpm < 0 || !wpm || wpm === Infinity ? 0 : wpm;
        
        wpmTag.innerText = wpm;
        mistakeTag.innerText = mistakes;
        cpmTag.innerText = charIndex - mistakes;
    } else {
        clearInterval(timer);
        inpField.value = "";
    }   
}

function initTimer() {
    if(timeLeft > 0) {
        timeLeft--;
        timeTag.innerText = timeLeft;
        let wpm = Math.round(((charIndex - mistakes)  / 5) / (maxTime - timeLeft) * 60);
        wpmTag.innerText = wpm;
    } else {
        clearInterval(timer);
    }
}

function resetGame() {
    loadParagraph();
    clearInterval(timer);
    timeLeft = maxTime;
    charIndex = mistakes = isTyping = 0;
    inpField.value = "";
    timeTag.innerText = timeLeft;
    wpmTag.innerText = 0;
    mistakeTag.innerText = 0;
    cpmTag.innerText = 0;
}

loadParagraph();
inpField.addEventListener("input", initTyping);
tryAgainBtn.addEventListener("click", resetGame);

paragraphs.js

const paragraphs = [
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla facilisi. Phasellus fermentum erat nec diam aliquet, vel gravida orci tincidunt. Vivamus eu enim quis elit sollicitudin cursus eget nec urna. In hac habitasse platea dictumst. Donec lacinia, libero nec bibendum venenatis, nisl nulla pellentesque magna, eu gravida lorem risus vel orci. Nunc nec dapibus felis. Vivamus sollicitudin semper ligula, nec blandit neque volutpat vel. Nullam auctor, leo vitae dapibus elementum, massa nunc vestibulum ex, ut convallis lectus tortor nec elit.",
    "Proin et maximus ante. Integer pellentesque eros ut felis posuere congue. Pellentesque varius, dui non faucibus congue, nulla purus efficitur eros, vel tempor leo lectus in velit. Aenean vel mauris non velit posuere finibus. Sed eget libero risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin nec bibendum est. Vivamus varius sollicitudin magna, nec blandit odio. Phasellus quis nisi id ipsum condimentum vestibulum.",
    "Suspendisse vehicula fermentum mauris, ut dictum mi sagittis sed. Aliquam pretium feugiat libero non tempor. Integer nec aliquam mi, id tincidunt velit. Integer ullamcorper urna sit amet dolor tincidunt, a sollicitudin sem molestie. Sed at odio nec ante sollicitudin ultrices. Nullam consectetur congue magna vel vehicula. Nullam porttitor elit eget convallis varius. Cras nec erat sed eros feugiat varius. Fusce nec nisi ac lorem vestibulum sodales.",
    "Curabitur auctor neque at augue faucibus, sit amet vestibulum quam aliquam. Etiam auctor, velit ut ultrices consectetur, metus justo laoreet dui, non dapibus quam magna eu justo. Phasellus tincidunt, felis vel mattis scelerisque, libero felis efficitur odio, eu vestibulum neque neque vel nisi. Vestibulum id nulla eget tortor vestibulum ultricies. Sed vel tincidunt quam, eget egestas neque. Mauris condimentum purus a nunc condimentum, vitae sodales lacus blandit. Sed dapibus hendrerit odio, ut euismod dui."
];

 

ALSO READ  Draggable HTML Element using pure JavaScript

Comments are closed.