Is there a way to have my typing test begin as soon as I press a key, without needing to click the start button, select the textbox, and then start typing?

The inspiration for my typing test came from a random quote API that I stumbled upon on GitHub. The main goal of this test is to have the timer start as soon as the first key is pressed.

If you're interested, you can check out the repository here: https://github.com/Tbscuddles/Tbswpmtest

Answer №1

Once the test begins, pay attention for the keydown event on the <textarea> and commence the timer:

userInput.addEventListener('keydown', timeReduce, { once: true });

Include once: true to trigger this event only one time.

Remember to also remove this listener in resetTest(), just in case it was not triggered:

userInput.removeEventListener('keydown', timeReduce, { once: true });

Putting all the pieces together:

//Random quotes api
const quoteApiUrl = "https://api.quotable.io/random?minLength=80&maxLength=100";
const quoteSection = document.getElementById("quote");
const userInput = document.getElementById("quote-input");

let quote = "";
let time = 60;
let timer = "";
let mistakes = 0;

//Display random quotes
const renderNewQuote = async() => {
  //Fetch content from quote api url
  const response = await fetch(quoteApiUrl);
  let data = await response.json();
  quote = data.content;

  //Array of chars in quote
  let arr = quote.split("").map((value) => {
    return "<span class='quote-chars'>" + value + "</span>";
  });
  quoteSection.innerHTML += arr.join("");
};

//Logic to compare input words with quote
userInput.addEventListener("input", () => {
  let quoteChars = document.querySelectorAll(".quote-chars");
  quoteChars = Array.from(quoteChars);

  //Array of user input chars
  let userInputChars = userInput.value.split("");
  //Loop through each char in quote
  quoteChars.forEach((char, index) => {
    //Check chars with quote chars
    if (char.innerText == userInputChars[index]) {
      char.classList.add("success");
    }
    //If user hasn't entered anything or backspaced
    else if (userInputChars[index] == null) {
      if (char.classList.contains("success")) {
        char.classList.remove("success");
      } else {
        char.classList.remove("fail");
      }
    }
    //if user entered wrong char
    else {
      if (!char.classList.contains("fail")) {
        //increment and displaying mistakes
        mistakes++;
        char.classList.add("fail");
      }
      document.getElementById("mistakes").innerText = mistakes;
    }

    //Return true if all chars are correct
    let check = quoteChars.every((element) => {
      return element.classList.contains("success");
    });

    //End test if all chars are correct
    if (check) {
      displayResult();
    }
    // or end test if atleast 80% of char are correct
  });

});

//Update timer
function updateTimer() {
  if (time == 0) {
    //End test if reaches 0
    displayResult();
  } else {
    document.getElementById("timer").innerText = --time + "s";
  }
}

//Set timer of the test
const timeReduce = () => {
  time = 60;
  timer = setInterval(updateTimer, 1000);
};

//End test
const displayResult = () => {
  //Display the result
  document.querySelector(".result").style.display = "block";
  clearInterval(timer);
  document.getElementById("stop-test").style.display = "none";
  userInput.disabled = true;
  let timeTaken = 1;
  if (time != 0) {
    timeTaken = (60 - time) / 100;
  }
  document.getElementById("wpm").innerText = (userInput.value.length / 5 / timeTaken).toFixed(2) + "wpm";
  document.getElementById("accuracy").innerText = Math.round(((userInput.value.length - mistakes) / userInput.value.length) * 100) + "%";
  wpmScore = (userInput.value.length / 5 / timeTaken).toFixed(2);
  document.getElementById("wpm").innerText = wpmScore + "wpm";
  document.getElementById("accuracy").innerText = Math.round(((userInput.value.length - mistakes) / userInput.value.length) * 100) + "%";
  document.getElementById("wpm-score").innerText = wpmScore;

};

//Start test
const startTest = () => {
  mistakes = 0;
  timer = "60";
  userInput.disabled = false;
  document.getElementById("start-test").style.display = "none";
  document.getElementById("stop-test").style.display = "block";
  document.getElementById("reset-test").disabled = false;
  userInput.addEventListener('keydown', timeReduce, { once: true });
}

//Reset test
function resetTest() {
  userInput.removeEventListener('keydown', timeReduce, { once: true });
  clearInterval(timer);
  time = 60;
  mistakes = 0;
  userInput.value = "";
  quoteSection.innerHTML = "";
  document.getElementById("mistakes").innerText = mistakes;
  document.getElementById("timer").innerText = time + "s";
  document.getElementById("reset-test").disabled = true;
  document.getElementById("start-test").disabled = false;
  document.getElementById("start-test").innerHTML = "Start Test";
  document.getElementById("stop-test").disabled = false;
  document.getElementById("stop-test").style.display = "none";
  document.getElementById("start-test").style.display = "block";
  document.getElementById("wpm").innerText = "";
  document.getElementById("accuracy").innerText = "";
  renderNewQuote();

}

// generate a new quote and render it  
window.onload = () => {
  userInput.value = "";
  document.getElementById("start-test").style.display = "block";
  document.getElementById("stop-test").style.display = "none";
  userInput.disabled = true;
  renderNewQuote();
}
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
body {
  background-color: #fff;
}

* {
  color: #000;
}

title {
  font-family: 'Lucida Sans';
}

h1,
h2,
h3 {
  margin: 0;
}

h1 {
  font-size: 24px;
  text-align: center;
}

h2 {
  font-size: 18px;
  text-align: center;
}

h3 {
  font-size: 16px;
  text-align: left;
}

* {
  font-family: 'Poppins', sans-serif;
  box-sizing: border-box;
}

.container {
  background-color: #f0f0f0;
}

.container {
  width: 80vmin;
  padding: 50px 30px;
}

.container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.container {
  border-radius: 10px;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}

.stats {
  font-size: 18px;
  text-align: center;
  font-family: 'Segoe UI';
}

.stats span {
  font-weight: 600;
}

#quote {
  text-align: justify;
...
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Rollup.js with Vue.js displays an HTML comment instead of rendering Vue HTML content

As I delve into the world of Vue.js, I am encountering some challenges with rendering a simple interpolation within my local development application. The Issue Strangely, the Vue instance displays an HTML comment of createElement <body> <sc ...

Laravel Mix fails to recognize VueJs integration in Laravel

Having an issue setting up VueJs with laravel 5.7 and mix where it keeps saying VueJs is not detected. I've already executed the npm install command. Below is my welcome view: <!doctype html> <html lang="{{ str_replace('_', '- ...

In Vue.js, modifying a parent component variable using $parent does not update the interpolation syntax

Child component <template> <div> <h3>Child Component</h3> <div> <button @click="changeValue()">Update Parent Value</button> </div> </div> </template> <script> export ...

Is it possible to replicate an HTML table using JavaScript without including the headers?

Discover a JavaScript snippet in this stack overflow post that allows you to create a button for automatically selecting and copying a table to the clipboard. When pasting the data into an Excel template, my users prefer not to include the header informat ...

User authentication using .pre save process

I have an API that accepts users posted as JSON data. I want to validate specific fields only if they exist within the JSON object. For example, a user object could contain: { "email" : "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" dat ...

Legends for Jqplot Donut Chart disappearing after saving changes

I am currently working on creating a donut chart using jqplot which displays legends outside the grid. However, I have encountered an issue where when attempting to save the chart as a PNG image, the resulting image shows empty legends. var imgData = $(&a ...

Pass a JavaScript variable to a PHP script using AJAX when a button is clicked, with a dynamically set href attribute

This is the current situation: There is a checkbox on each row of a table that represents some information An initially disabled button becomes enabled when any checkbox is checked The button is enclosed by an <a></a> tag as shown: <a>&l ...

Is there a way to obtain the tasklist result of exec() from child_process and convert it to JSON format?

When I use the tasklist command with child_process, the stdout returns processes in Unicode string format that is not easily queryable. Here is the code snippet: var exec = require('child_process').exec; ... exec('tasklist', function( ...

Link Quick Techniques

I currently have an Express server set up with CRUD methods My goal is to automatically trigger the get method whenever a post, put, or delete request is made. This will ensure that the view on the Front-end side gets updated accordingly. Below is an exc ...

Why isn't my onScroll event triggering in my React.js application? What mistake am I making?

I am facing an issue with my onScroll event in react js. My goal is to implement infinite scrolling in react js, but unfortunately, the onScroll event is not triggering as expected. The process involves fetching posts from an API and passing them to the ...

How can I send various maximum values to the Backbone template?

Although in theory, it may seem simple, I am unsure about the exact code to use. In a view, if I define a variable max that retrieves an attribute called points from a collection, I can pass this variable as an object into my template using maxPoints.toJS ...

Once the timer has finished, I would like for a specific component to be displayed

I am trying to make my component CountDownSquare disappear once the timer has fully counted down. In my homePageData, I have the main text stored in a h3 element that should only appear once the countdown is complete. I attempted to use a ternary stateme ...

How to handle .find errors in Mongoose, Node.js, and Express

I am attempting to perform a search with mongoose, but I am encountering the following error message: "TypeError: Query.find is not a function" Below is the model I am using: // file: ./models/request.js var mongoose = require('mongoose'), ...

Switch between Fixed and Relative positions as you scroll

Just a small adjustment is needed to get it working... I need to toggle between fixed and relative positioning. JSFiddle Link $(window).on('scroll', function() { ($(window).scrollTop() > 50) ? ($('.me').addClass('fix ...

How to append a JSON object to an existing .json file

UPDATE: Despite successfully executing the PHP code, my JSON file remains unchanged. I must apologize in advance for covering old ground, but I have spent countless hours exploring different solutions with no success. Perhaps sharing my challenge could as ...

Encountering a 'Not Found' error while deploying my React Node application with an AWS-hosted database on Heroku

const express = require("express"); const app = express(); const bodyParser = require("body-parser"); const port = process.env.PORT || 3002; const cors = require("cors"); const path=require("path"); const routing = ...

The functionality of the Node.js/Express.js app is limited to operating solely on Port 3000

I'm currently troubleshooting an issue with my Node.js/Express.js app running on my server. It seems to only work on port 3000, and I'm trying to understand why. Here's what I've discovered: When I don't specify a port (app.liste ...

Issue with Selenium webdriver's element.click() method not operating as anticipated in Chrome while using Mocha framework

While testing the log in feature of a website, I encountered an issue where the .click() method did not perform as expected despite being able to locate the Login button. Here is the relevant JavaScript test code: driver.sleep(1000) driver.findElement(By ...

Trigger a click event upon page load using jQuery or JavaScript

I tried searching for this functionality on a different website, but it didn't work on my site. Can you help me figure out how to trigger a click event on page load? On one of my pages, a popup box appears when I click on a "project" link. Here is th ...

Omit specific TagNames from the selection

How can I exclude <BR> elements when using the statement below? var children = document.getElementById('id').getElementsByTagName('*'); Is there a special syntax for getElementsByTagName that allows me to achieve this, or is the ...