Building a Typing Speed Test App with JavaScript
Welcome back to our JavaScript series! In this 88th installment, we're going to tackle an engaging and practical project: building a fully functional typing speed test application. This project is excellent for solidifying your DOM manipulation skills, understanding event handling, working with timers, and performing calculations with JavaScript. Not only will you create a useful tool, but you'll also build a project that looks great in your portfolio!
Project Overview: What We'll Build
Our typing speed test app will present users with a random piece of text. As the user types, the application will:
- Start a timer when the user begins typing.
- Visually indicate correct and incorrect characters as they are typed.
- Automatically stop the timer and calculate results once the user finishes the text.
- Display performance metrics: Words Per Minute (WPM) and Accuracy Percentage.
- Provide a "Restart" button to try again with a new text.
Setting Up the HTML Structure
First, let's lay out the basic structure of our application using semantic HTML. We'll need a container for the text to be typed, an input field for the user, a display area for results and the timer, and a button to restart the test.
<div class="container">
<h2>Typing Speed Test</h2>
<div class="display-text-area">
<!-- Text to be typed will appear here -->
<span id="display-text">Click 'Start' or start typing to begin...</span>
</div>
<textarea id="text-input" placeholder="Start typing here..." autofocus></textarea>
<div class="results">
<p>Time: <span id="timer">0s</span></p>
<p>WPM: <span id="wpm">0</span></p>
<p>Accuracy: <span id="accuracy">0%</span></p>
</div>
<button id="reset-button">Restart</button>
</div>
A bit of CSS would make this look presentable, but for the scope of this tutorial, we'll focus primarily on the JavaScript logic. Imagine some basic styling that centers elements, gives them appropriate sizes, and highlights correct/incorrect text.
Bringing it to Life with JavaScript
Now for the core logic! We'll break down the JavaScript into several manageable pieces, starting with selecting our DOM elements and setting up initial data.
1. Selecting DOM Elements & Initial Setup
We'll need references to all the interactive and display elements from our HTML. We'll also define an array of quotes that our app will use for the typing tests.
// Select DOM elements
const displayTextSpan = document.getElementById('display-text');
const textInput = document.getElementById('text-input');
const timerSpan = document.getElementById('timer');
const wpmSpan = document.getElementById('wpm');
const accuracySpan = document.getElementById('accuracy');
const resetButton = document.getElementById('reset-button');
// Array of quotes for the typing test
const quotes = [
"The quick brown fox jumps over the lazy dog.",
"Practice makes perfect. The more you type, the faster you'll become.",
"JavaScript is the world's most popular programming language.",
"Learning to code opens up a world of possibilities and problem-solving.",
"Success is not final, failure is not fatal: it is the courage to continue that counts."
];
// State variables
let startTime;
let intervalId;
let currentQuote = "";
let charactersTyped = 0;
let testStarted = false;
2. Displaying the Text
We need a function to pick a random quote from our array and display it in the display-text area. Each character of the quote will be wrapped in a <span> for individual styling later (e.g., highlighting correct/incorrect input).
function loadText() {
const randomIndex = Math.floor(Math.random() * quotes.length);
currentQuote = quotes[randomIndex];
displayTextSpan.innerHTML = ''; // Clear previous text
currentQuote.split('').forEach(char => {
const charSpan = document.createElement('span');
charSpan.innerText = char;
displayTextSpan.appendChild(charSpan);
});
textInput.value = ''; // Clear input field
textInput.focus(); // Focus the input field
timerSpan.innerText = '0s';
wpmSpan.innerText = '0';
accuracySpan.innerText = '0%';
clearInterval(intervalId); // Ensure no timer is running
testStarted = false;
charactersTyped = 0;
}
3. Starting the Test & Timer
The test should begin when the user starts typing. We'll use setInterval to update the timer every second.
function startTest() {
if (!testStarted) {
testStarted = true;
startTime = new Date().getTime(); // Record start time
intervalId = setInterval(updateTimer, 1000); // Update timer every second
}
}
function updateTimer() {
const currentTime = new Date().getTime();
const elapsedTime = Math.floor((currentTime - startTime) / 1000); // Time in seconds
timerSpan.innerText = `${elapsedTime}s`;
}
4. Handling User Input and Comparison
This is where the real-time feedback happens. As the user types, we compare their input character by character with the currentQuote. We'll update the styling of each character span accordingly.
function checkInput() {
startTest(); // Ensure timer starts on first input
const inputText = textInput.value;
const quoteCharacters = displayTextSpan.querySelectorAll('span');
let correctChars = 0;
let totalChars = inputText.length;
quoteCharacters.forEach((charSpan, index) => {
const char = currentQuote[index];
if (index < inputText.length) {
if (inputText[index] === char) {
charSpan.classList.remove('incorrect');
charSpan.classList.add('correct');
correctChars++;
} else {
charSpan.classList.remove('correct');
charSpan.classList.add('incorrect');
}
} else {
// Characters not yet typed
charSpan.classList.remove('correct', 'incorrect');
}
});
charactersTyped = inputText.length;
// Check if the user has finished typing
if (inputText.length === currentQuote.length) {
calculateResults(correctChars);
clearInterval(intervalId); // Stop the timer
textInput.disabled = true; // Disable input after test completion
testStarted = false;
}
}
Note: You'd need CSS rules for .correct and .incorrect classes to provide visual feedback (e.g., green for correct, red for incorrect).
5. Calculating Results: WPM and Accuracy
Once the test is complete, we calculate the Words Per Minute (WPM) and accuracy percentage. WPM is typically calculated as (characters / 5) / minutes.
function calculateResults(correctChars) {
const currentTime = new Date().getTime();
const elapsedTimeSeconds = (currentTime - startTime) / 1000;
const elapsedTimeMinutes = elapsedTimeSeconds / 60;
// WPM calculation: (total correct characters / 5) / minutes
// We use charactersTyped for WPM, as it represents the effort,
// and correctChars for accuracy.
const wordsTyped = charactersTyped / 5;
const wpm = Math.round(wordsTyped / elapsedTimeMinutes);
// Accuracy calculation: (correct characters / total typed characters) * 100
const accuracy = charactersTyped > 0
? Math.round((correctChars / charactersTyped) * 100)
: 0;
wpmSpan.innerText = isNaN(wpm) ? '0' : wpm;
accuracySpan.innerText = isNaN(accuracy) ? '0%' : `${accuracy}%`;
}
6. Resetting the Test
The reset function should bring the app back to its initial state, ready for a new test.
function resetTest() {
clearInterval(intervalId); // Stop any running timer
loadText(); // Load a new random quote
textInput.disabled = false; // Enable input
textInput.value = ''; // Clear input field
timerSpan.innerText = '0s';
wpmSpan.innerText = '0';
accuracySpan.innerText = '0%';
testStarted = false;
charactersTyped = 0;
// Clear any previous highlighting
displayTextSpan.querySelectorAll('span').forEach(span => {
span.classList.remove('correct', 'incorrect');
});
}
7. Putting It All Together: Event Listeners
Finally, we attach our functions to the appropriate events. The text-input field will listen for input events, and the reset-button will listen for click events.
// Event Listeners
textInput.addEventListener('input', checkInput);
resetButton.addEventListener('click', resetTest);
// Initial load
loadText();
Enhancements and Next Steps
This is a solid foundation, but there's always room for improvement and new features! Consider these ideas to take your typing test app to the next level:
- Difficulty Levels: Offer different text lengths or complexity levels.
-
High Score Tracking: Use
localStorageto store and display the user's best WPM. - More Quotes: Integrate an API to fetch new quotes dynamically.
- Countdown Timer: Add a short countdown before the test begins.
- Visual Keyboard: Display a virtual keyboard and highlight keys as they are pressed.
- Responsiveness: Ensure the layout adapts well to different screen sizes.
Conclusion
Congratulations! You've successfully built a functional typing speed test application using JavaScript. This project reinforces several fundamental concepts of web development, from DOM manipulation and event handling to logic and calculations. Experiment with the enhancements suggested above, or come up with your own creative additions. Happy coding, and keep practicing your JavaScript skills!