You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

388 lines
34 KiB

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My Learning Website</title>
<link href="/styles/styles.css" rel="stylesheet" type="text/css">
<link href="/webdevelopment/styles/styles.css" rel="stylesheet" type="text/css">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<article>
<h1 class="coursetitle">JavaScript Essential Training</h1>
<h2 class="lecturer">LinkedIn Learning : Morten Rand-Hendriksen</h2>
<h1 class="episodetitle">Project: Typing Speed Tester</h1>
<h2 class="sectiontitle">Rundown of HTML Markup</h2>
<p>As a starting point, here, we have a fairly standard web page with some normal components as we can see in the image shown below. The HTML code is shown in figure 89 and the webpage itself is shown in figure 90.</p>
<pre><code>
1. &lt;!DOCTYPE html&gt;
2. &lt;html lang="en-US"&gt;
3.
4. &lt;head&gt;
5. &lt;meta charset="UTF-8"&gt;
6. &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
7. &lt;title&gt;JS Typing Speed Test&lt;/title&gt;
8. &lt;script src="script.js" async&gt;&lt;/script&gt;
9. &lt;link rel="stylesheet" href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,700,700i,900,900i" type="text/css" media="all"&gt;
10. &lt;link rel="stylesheet" href="style.css"&gt;
11. &lt;/head&gt;
12.
13. &lt;body&gt;
14. &lt;header class="masthead"&gt;
15. &lt;h1&gt;Test Your Typing Speed&lt;/h1&gt;
16. &lt;/header&gt;
17. &lt;main class="main"&gt;
18. &lt;article class="intro"&gt;
19. &lt;p&gt;This is a typing test. Your goal is to duplicate the provided text, EXACTLY, in the field below. The timer starts when you start typing, and only stops when you match this text exactly. Good Luck!&lt;/p&gt;
20. &lt;/article&gt;&lt;!-- .intro --&gt;
21. &lt;section class="test-area"&gt;
22. &lt;div id="origin-text"&gt;
23. &lt;p&gt;The text to test.&lt;/p&gt;
24. &lt;/div&gt;&lt;!-- #origin-text --&gt;
25.
26. &lt;div class="test-wrapper"&gt;
27. &lt;textarea id="test-area" name="textarea" rows="6" placeholder="The clock starts when you start typing."&gt;&lt;/textarea&gt;
28. &lt;/div&gt;&lt;!-- .test-wrapper --&gt;
29.
30. &lt;div class="meta"&gt;
31. &lt;section id="clock"&gt;
32. &lt;div class="timer"&gt;00:00:00&lt;/div&gt;
33. &lt;/section&gt;
34.
35. &lt;button id="reset"&gt;Start over&lt;/button&gt;
36. &lt;/div&gt;&lt;!-- .meta --&gt;
37. &lt;/section&gt;&lt;!-- .test-area --&gt;
38. &lt;/main&gt;
39.
40. &lt;/body&gt;
41.
42. &lt;/html&gt;
</code></pre>
<p class="caption">Figure 89 - the HTML code for the Typing Speed Tester</p>
<img src="file:///C|/Users/emuba/AppData/Local/Temp/Adobe/Dreamweaver 2021/images/image45.png" alt="Figure 90 image">
<p class="caption">Figure 90 - the web page</p>
<p>The HTML components are a header with an &lt;h1&gt; title, a box of text inside an &lt;article&gt; tag that acts sa an introduction, a box displaying some text, a box into which the user can type text, a clock and a button.</p>
<p>We don't have any functionality so at the moment, this is a static web page but it is complete as far as the HTML is concerned. Functionality will be added with JavaScript.</p>
<p>Going back to the HTML for a second, notice that the different elements have class names which we will use for styling purposes. We will also use these to reference the elements from our JavaScript code. The starting point for the JavaScript code is shown in figure 91.</p>
<pre><code>
1. const testWrapper = document.querySelector(".test-wrapper");
2. const testArea = document.querySelector("#test-area");
3. const originText = document.querySelector("#origin-text p").innerHTML;
4. const resetButton = document.querySelector("#reset");
5. const theTimer = document.querySelector(".timer");
</code></pre>
<p class="caption">Figure 91 - the JavaScript code</p>
<p>This is pretty empty at the moment, with just five constants which reference (via the class-name) the HTML elements that we are going to be interacting with.</p>
<p>In particular, note that originText maps to the innerHTML of the &lt;p&gt; element which is inside the element with the class name, origin-text. So, this is referencing the actual text used for the test rather than the box that it sits in.</p>
<p>When we interact with the app, in other words, when we start the test, several things will happen. The timer will start and the text in the test area will be compared to the text in the original text area. The timer stops when both of these match. In addition, the test will reset itself if we click the "Start Over" button.</p>
<h1 class=>Use Event Listeners to Detect Typing</h1>
<p>There are two elements that the user interacts with directly and we will use event listeners to detect when this has happened. The two elements are the area for keyboard input and the reset button.</p>
<p>For the input text area, the event is triggered when the user presses a key in that area. In other words, the user types something into the input box. When that happens, the timer is started and we start to match the text in the input area with the test text.</p>
<p>To add the listener, we want to add it to the input box which is referenced by the constant, testArea, in the JavaScript. So the code for this is</p>
<pre><code>
testArea.addEventListener("keypress", start, false);
</code></pre>
<p>The start function is called to run the timer and false represents whether the text matches. The test stops when this value is set to true.</p>
<p>1The start function will start the timer so it needs to know when something has been typed in the input box and it can do this quite simply be measuring the length of the text there. We will create a variable called textEnteredLength to hold this value and then output it to the console log. This gives us the following.</p>
<pre><code>
1. function start() {
2. let textEnteredLength = testArea.value.length;
3. consolt.log(textEnteredLength);
4. }
</code></pre>
<p class="caption">Figure 92 - the start function</p>
<p>If we type a single letter into the input area, we will now see the length of the text being output to the console, but this gives an output zero. If we continue until 6 characters have been typed, the output is 5.</p>
<p>This is because the event is triggered when the key is pressed and before the character is added to the box. As a result, we can't really use this value for comparing the text to the test text. What I mean by that is that when the key is pressed, the value held inside the test area is going to be everything typed by the user except the last character. To solve this problem, we will set up a second event listener that kicks in when the key is released and so this happens after the character has been added to the text in the test area.</p>
<pre><code>
1. testArea.addEventListener("keyup", spellCheck, false);
</code></pre>
<p>This is similar to the previous event handler, but this time it calls the spellCheck function.</p>
<p>In the spellCheck function, we will create a variable called textEntered and this is going to hold the actual text the user has typed into the test area.</p>
<pre><code>
1. function spellCheck() {
2. let textEnteredLength = testArea.value;
3. console.log(textEntered);
4. }
</code></pre>
<p class="caption">Figure 93 - the spellCheck function</p>
<p>Again, this is quite similar to the start function except for the variable name. Now, if I type some text into the input area, I will see that the length is being logged to the console by the start function and the actual text displayed in the input area is being logged to the console by the spellCheck function. This output is shown in figure 94.</p>
<img src="images/image46.png" alt="Figure 94 image">
<p class="caption">Figure 94 - the values of textEnteredLength and textEntered being output to the console every time a character is typed inside the input area</p>
<p>We can see that every time a character is typed inside the input area, the variables are updated. Each time, the value of textEnteredLength is one less than the number of key presses, but the value of textEntered exactly matches the text in the input area so we can use this to compare our typed text against the test text.</p>
<p>The final event listener we want to add is for the reset button. We will set this up to be triggered when the button is clicked and, for the time being, we will just have this log to the console a confirmation that the button has been clicked.</p>
<p>So, the event handler is</p>
<pre><code>
1. resetButton.addEventListener("click", reset, false);
</code></pre>
<p>The function is simply</p>
<pre><code>
1. function reset() {
2. console.log("The reset button has been clicked!");
3. }
</code></pre>
<p class="caption">Figure 95 - the reset function</p>
<p>We can quickly test to see that the message is being logged to the console when we click the button.</p>
<h1 class="sectiontitle">Build a Count-Up Timer</h1>
<p>The clock is going to count up from zero when the user starts to enter text in the input area. The time is expressed in minutes, seconds and hundredths of a second. JavaScript has an interval function that allows you to call a function for every specified time unit - that is, every thousandth of a second, every hundredth of a second and so on and this will be very useful.</p>
<p>We already have a start function that is called whenever a character is pressed in the input area, but we only want the time to start when the first key is pressed and you will recall that when that happens, the length of the string in the input area is 0.</p>
<p>We can add a conditional statement in the start function and this will do two things. Firstly, it will set the interval value and then it will set the call to the function that operates the timer. We will set the value of the interval (which is expressed as thousandths of a second) to 10 so the function will be called every hundredth of a second.</p>
<p>The amended start function is therefore</p>
<pre><code>
1. function start() {
2. let textEnteredLength = testArea.value.length;
3. if (textEnteredLength==0) {
4. setInterval(runTimer, 10);
5. }
6. console.log(textEnteredLength);
7. }
</code></pre>
<p class="caption">Figure 96 - the amended start function</p>
<p>Line 4 is where we are setting the interval and calling the function. More information on this can be found in the <a href=" https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">MDN Web Docs</a>. In its simplest form, setInterval takes two arguments. The first is the name of the function to be called, in this case, the runTimer function and the second is the interval.</p>
<p>Before we start to write the runTimer function, we will create a variable called timer. In the runTimer function, we will grab the text from the timer displayed on the HTML page and set it to the value of timer. We will then start incrementing it.</p>
<p>Now, this isn't going to give us, by any means, the completed functionality. All it will do is start counting every one hundredth of a second and incrementing the counter so the timer will simply display a counter showing the number of hundredths of seconds that have elapsed. But it is useful to do this because we can make sure that it is working in the sense that it is counting off time and updating the timer display. Also, of course, we should be able to see that it starts counting when a character is typed in the input area.</p>
<p>The code to create the variable is</p>
<pre><code>
var timer=0;
</code></pre>
<p>and we will put that right after our constants and before our first function definition. Notice that we have initialized it to zero.</p>
<p>In the runTimer function, we are just going to set the text in the timer box to the value of timer and increment it so every one hundredth of a second, we will set it to the incremented value and increment it again.</p>
<p>The runTimer function is therefore</p>
<pre><code>
1. function runTimer() {
2. theTimer.innerHTML = timer;
3. timer++;
4. }
</code></pre>
<p class="caption">Figure 97 - the runTimer function</p>
<p>Actually, when I did this, I had a couple of simple errors in both the start function and the runTimer function which are worth mentioning because they do demonstrate the fact that it is worthwhile to develop in this way. That is, add a little functionality at a time and confirm that it is working before adding the more complex functionality.<p>
<p>As a matter of interest, the error in the start function was that I used an assignment operator in the conditional statement, so the line was typed as</p>
<pre><code>
if (textEnteredLength=0) {
</code></pre>
<p>This is probably quite a common error and can be a nuisance to track down. The error in the start function is that I was setting the innerHTML on timer rather than timer which doesn't do anything.</p>
<p>Both of these errors can be tricky because they are valid code so these are logical rather than syntactical errors. In any case, once these were fixed, the timer started to work as expected and this is shown in figure 98.</p>
<img src="images/image47.png" alt="Figure 98 image">
<p class =>Figure 98 - the website with the setTimer function working"</p>
<p>We can see, in figure 98, that the timer has been running for just over 300 hundredths of a second, so just over three seconds. It started the timer when I started typing in the input area, so everything seems to be working as expected so far.</p>
<p>Now, we want to think about how to store the time or manipulate the time so that we can display the different components of the timer (minutes, seconds and hundredths of a second). To keep things simple for display purposes, we will replace the timer variable with a timer array so we will initialize this with</p>
<pre><code>
var timer=[0,0,0,0];
</code></pre>
<p>The first element in the array stores the time in minutes, the second stores the seconds, the third stores hundredths of a second and the fourth stores the total time elapsed in hundredths of a second.</p>
<p>Inside the runTimer function, rather than setting the innerText of current timer to the value of timer, we are going to set it to a string value that represents the properly formatted output.</p>
<p>We first create that string with</p>
<pre><code>
let currentTime = timer[0] + ":" + timer[1] + ":" + timer[2];
</code></pre>
<p>and we will set the innerText of theTimer with this string</p>
<pre><code>
theTimer.innerHTML = currentTime;
</code></pre>
<p>Instead of simply incrementing the timer, we will increment the value of timer[3], that is, we are counting up thousandths of a second. We will then use that value to calculate the values for the other elements in the array. For timer[0],this is</p>
<pre><code>
timer[0] = Math.floor((timer[3]/100)/60);
</code></pre>
<p>The maths is fairly straightforward. We are taking the total time in hundredths of a second and dividing it by 100 to give the time in seconds. We then divide this by 60 to give the time in minutes. The Math.floor function takes this value and discards the decimal part to give the total number of whole minutes elapsed so far. For example, let's say the total time so far is 23,500. Dividing this by 100 gives 235 (which is the time in seconds) and dividing this by 60 gives about 3.9. So this corresponds to a time of almost four minutes. The Math.floor function will convert this to three since we have not yet completed the fourth minute.</p>
<p>For timer[1], we need to convert the time into seconds as before, but this time, we want to subtract the number of minutes so that we only get the seconds, in effect for the current minute.</p>
<pre><code>
timer[1] = Math.floor((timer[3]/100) - time[0] * 60);
</code></pre>
<p>As before, we are converting the elapsed time to seconds by dividing the total elapsed time by 100. Let's use 23500 hundredths of a second as an example again (this actually equates to a time of 3 minutes and 55 seconds). Dividing 23500 by 100 gives us 235 seconds. We have already set the value of timer[0] to 3 for the minutes so we can subtract (from 235) 3 * 60. In other words, we are converting the minutes back to seconds (180 seconds) which tells us that 180 of our 235 seconds are being represented by timer[0]. Subtracting 180 from 235 gives us 55 so we set the value of timer[1] to 55. Again, the Math.floor function ensures that this value is only the number of whole seconds.</p>
<p>Similarly for timer[2], we take the total elapsed time in hundredths of a second. We don't need to divide by 100 because we don't want to convert the time into seconds. However, just as we converted the value of timer[0] to seconds in order to calculate the value to be stored in timer[1], we will need to convert it to hundredths of a second so we can take away the whole minutes from the value of timer[2] and we also need to do the same for timer[1] so that we can subtract the number of whole seconds.</p>
<p>For reference, the runTimer function with all these changes added is shown in figure 99.</p>
<pre><code>
1. function runTimer() {
2. let currentTime = timer[0] + ":" + timer[1] + ":" + timer[2];
3. theTimer.innerHTML = currentTime;
4. console.log(currentTime);
5. timer[3]++;
6.
7. timer[0] = Math.floor((timer[3]/100)/60);
8. timer[1] = Math.floor((timer[3]/100) - timer[0] * 60);
9. timer[2] = Math.floor(timer[3] - (timer[1] * 100) - timer[0] * 100 * 60);
10. }
</code></pre>
<p class="caption">Figure 99 - the runTimer function</p>
<p>We can save this and refresh the page. The result should be that the functionality is nearly complete. The timer starts to run when something is typed in the input area and it does seem to display the time in minutes, seconds and hundredths of a second. However, there is one small problem with the display as demonstrated in the image shown in figure 100.</p>
<img src="images/image48.png" alt="Figure 100 image">
<p class =>Figure 100 - the website with most of the functionality added</p>
<p>In this example, 7.22 seconds have elapsed and the timer seems to be displaying the timer correctly. But we really want it to display three 2-digit numbers so the timer really should look like</p>
<pre><code>
00:07:22
</code></pre>
<p>We can fix this, and we will find out how to do that in the next section.</p>
<h1 class="sectiontitle">Build a Helper Function for Leading Zeros</h1>
<p>In order to ensure that each part of our timer displays a two digit number, we will create a helper function. At the moment, we are constructing the timer string like this.</p>
<pre><code>
let currentTime = timer[0] + ":" + timer[1] + ":" + timer[2];
</code></pre>
<p>Each element of timer is going to return a number between 0 and 99 in the case of timer[2], 0 and 59 in the case of timer[1] and anything from 0 up in the case of timer[0]. Note that with timer[0], we haven't bothered to consider what would happen if the number of minutes exceeded 59. We are not going so far as to count hours in our timer so if we leave it running for long enough, we might see some fairly large numbers in the minutes part put this is probably fair enough since with normal use, we wouldn't expect it to run for more than a few minutes.</p>
<p>It therefore seems reasonable to assume that minutes will never exceed 59 (that is, the timer will not be running for long enough for us to count hours). That is essentially a design decision, but it does seem perfectly reasonable.</p>
<p>So, we have our numbers and we want our helper function to convert the number in an appropriate way for output. We will call the function leadingZeros and pass each element of timer to it before adding it to the string. As a result, the code for construction the string becomes</p>
<pre><code>
let currentTime = leadingZero(timer[0] )+ ":" + leadingZero(timer[1]) + ":" + leadingZero(timer[2]);
</code></pre>
<p>In each case, we are passing a number to leadingZeros and we want leadingZeros to return it as a two digit number with a leading zero if necessary.</p>
<p>Since we know that a number is being passed to leadingZeros such as 7 and this is being returned as a two digit number, in this case 07, it seems reasonable to assume that this is going to be returned as a string. If we returned 07 as a numeric value, even if that were possible (in arithmetical terms, adding a zero to a number isn't going to change it), JavaScript would ignore the leading zero.</p>
<p>However, we need the leading zero in order to create the proper output for our timer, so it is actually going to be converted to a string at some point.</p>
<p>The first thing that the leadingZeros function will do with a number passed to it is to test it to see how many digits it has. If it is less than 10 (that is, a single digit number), we can convert it to a string and add a leading zero with a single operation by performing a string concatenation with "0" and our single digit, so 7 would become "07".</p>
<p>We then return the number. If the number had been, let's say, 27, we would not perform this operation since it is not less than 10, and the value 27 is passed back. This gives us the following function.</p>
<pre><code>
1. function leadingZero(time) {
2. if (time &lt;= 9) {
3. time = "0" + time;
4. }
5. return time;
6. }
</code></pre>
<p class="caption">Figure 101 - the leadingZeros function.</p>
<p>Again, we can save this and refresh the browser and we will see that the timer still works correctly, but it displays the time correctly with the leading zeros if necessary. So, for example, the screenshot shown in figure 102 shows the output when the timer is 5 minutes and 5 seconds (with 74 hundredths of a second).</p>
<img src="images/image49.png" alt="Figure 102 image">
<p class="cap">Figure 102 - the output with the timer displaying correctly with leading zeros</p>
<h1 class="sectiontitle">Detect Spelling Errors by Matching Strings</h1>
<p>For the application to be useful, we need some method of determining whether the text typed by the user is correct. That is, does it match the test text. We also need to feed this back to the user so they know how they are doing and a nice, visual way to do that would be to change the colour of the border for the text input area. This allows the user to easily see what the state of their text is while still focussing on the text.</p>
<p>You might expect that this to have two possible states, right or wrong (either the text matches or it doesn't). But there are actually three.</p>
<ul>
<li>Is the text complete and correct (in which case, the test should finish).</li>
<li>Is the text correct but incomplete (in which case, the test should continue).</li>
<li>Is the text incorrect (if the text is incorrect, the test is incomplete regardless of whether the user thinks he or she has typed the complete text.</li>
</ul>
<p>It is important to include the state where the text is correct but not yet complete. If we didn't, the user wouldn't know whether the text was correct until the entire text had been typed and we don't want that.</p>
<p>The current state of the spellCheck function is shown below.</p>
<pre><code>
1. function spellCheck() {
2. let textEntered = testArea.value;
3. // console.log(textEntered);
4. }
</code></pre>
<p class="caption">Figure 103 - the starting point for the spellCheck function</p>
<p>We have created a variable and set it to the value in the input box (the testArea) so this is the text that the user has typed. We already have a constant which holds the text in the test area and that is originText. However, we don't necessarily want to compare the text in the test box with this because it may not be complete. Instead, what we want to do is compare the text typed by the user with the same length of text in the original text.</p>
<p>In other words, if the user has typed three characters, we only want to compare this with the first three characters of the test text.</p>
<p>To do this, we will create another variable with the text we will use with the comparison and we will call it originTextMatch. We will set this value to a substring from the original text as follows.</p>
<pre><code>
let originTextMatch = originText.substring(0, textEntered.length);
</code></pre>
<p>Note that the substring will always start from the first character in the test text and the length should be the same as the current length for textEntered, so it makes sense to use that as the length of the substring.</p>
<p>The testing will run something like this. We will first check the string, textEntered, to see if it matches the whole of the test string. If it does, the border colour changes to green and the test is complete.</p>
<p>If not, we will do another test to see if textEntered matches a subset of the test text (in other words, if it is partially correct) and the border colour changes to orange.</p>
<p>If this test also fails, the border colour changes to blue.</p>
<p>This gives us a spellCheck function as follows.</p>
<pre><code>
1. function spellCheck() {
2. let textEntered = testArea.value;
3. let originTextMatch = originText.substring(0, textEntered.length);
4. if (textEntered == originText) {
5. testWrapper.style.borderColor = "#429890";
6. } else if textEntered == originText {
7. testWrapper.style.borderColor = "#65CCF3";
8. } else {
9. testWrapper.style.borderColor = "#E95D0F";
10. }
11. // console.log(textEntered);
12. }
</code></pre>
<p class="caption">Figure 104 - the completed (for now) spellCheck function</p>
<p>I have tested this and it seems to be working perfectly. However, the test doesn't stop when it should and we will look at that next.</p>
<h1 class="sectiontitle">Stop the Timer When the Test is Done</h1>
<p>We want to stop the timer when the test is complete, however, this presents a little bit of a challenge. Remember that the timer is running because we set an interval inside the start function which calls the runTimer function every hundredth of a second. We can stop it by changing the interval, but we have to do it inside the start function because that is where we set the interval. If we try to do this from, for example, the spellCheck function, we would simply be creating a second interval and this would not stop the timer.</p>
<p>There is a simple solution. We need to add a new variable called interval which we will put after the timer variable we created earlier. We are going to use this as a reference to the function for which we set the interval. In the start function, we are creating the interval with the statement</p>
<pre><code>
setInterval(runTimer, 10);
</code></pre>
<p>We want to change this to</p>
<pre><code>
interval=setInterval(runTimer, 10);
</code></pre>
<p>If we go back to our spellCheck function, the first conditional statement checks whether the text in the input box matches the test text. At the moment, it just changes the border colour of the testWrapper to green. We want to add this line just before the line to change the border colour.</p>
<pre><code>
clearInterval.interval;
</code></pre>
<p>Since we are using the reference, interval, this is clearing the interval from the function this refers to, so it seems to do the trick.</p>
<p>We can test that and it does seem to work. When we start to type in the input box, the timer starts running and when the test is complete, it stops.</p>
<p>However, if we delete the text and start to type again, something a little strange happens. The timer starts running again. This is because the timer has been set up to start whenever there is text in the input box (or rather, as soon as a character has been typed but the length of the string in the text area is 0). So, if we delete the text and then start typing again, the timer starts running again.</p>
<p>To get around this, we want to be able to detect when the timer has already been started. To do this, we can introduce a new variable called timerRunning which we will create as a global variable so this line goes right after the declaration of the interval variable.</p>
<pre><code>
var timerRunning = false;
</code></pre>
<p>In the start function, we can now check whether the timer has already been started by changing the conditional statement to</p>
<pre><code>
if (textEnteredLength===0 && !timerRunning) {
</code></pre>
<p>In the block of code that is executed when this condition evaluates to true, we want to toggle the value of timerRunning to show that it has been started so we will add this statement</p>
<pre><code>
timerRunning=true;
</code></pre>
<p>In the course video, this seems to work although I'm not sure how this happens because once the value of timerRunning is set to true, I can't see that it is being set to false again and so the timer shouldn't restart unless the browser is refreshed.</p>
<p>When I test it, this does seem to be the case. I will, perhaps, make an attempt to fix this at some later time.</p>
<h1 class=>Add a Reset Button</h1>
<p>The last thing we need to do is to implement the reset button so that the user can reset everything if they want to redo or restart the test. We already created an event listener to respond to a click on the 'Start over' button, but we just implemented that with a placeholder that simply logs a message to the console to confirm when the button has been clicked.</p>
<p>We will expand on that, now, by causing everything to be reset when the button is clicked. We will start by clearing the interval to make sure that isn't still running in the background.</p>
<pre><code>
clearInterval(interval);
</code></pre>
<p>We also want to set the interval to null.</p>
<pre><code>
interval=null;
</code></pre>
<p>The reason we do this is because we want to avoid the browser creating multiple instances of the interval which can waste resources.</p>
<p>We also want to reset the timer.</p>
<pre><code>
timer = [0,0,0,0];
</code></pre>
<p>To allow our timer to start running again (remember that we set it up so that it would only start if timerRunning had a value of false), we also need to reset the value of timerRunning.</p>
<pre><code>
timerRunning = false;
</code></pre>
<p>We also want to set the text in the test area to empty.</p>
<pre><code>
testArea.value="";
</code></pre>
<p>Although we reset the timer, we haven't updated the timer display so we can do that with</p>
<pre><code>
theTimer.innerHTML = "00:00:00";
</code></pre>
<p>Finally, we will set the border colour of the test area to grey.</p>
<pre><code>
testWrapper.style.borderColor = "grey";
</code></pre>
<p>Once we start a test, this border colour will change to either blue or orange and then finally to green, assuming the text is typed correctly and is complete. So, the only time the border is grey is when the test starts and before the user has typed anything in the test area, so this also acts as a visual confirmation that the test has been reset to its initial state.</p>
<p>So, this now works pretty much as expected and is a functional typing speed test. It's worth pointing out that the test has been set up with a fairly simple piece of text for the user to type. There is no way to change this in the browser, but we can change it in our code and for this, we need to go back to the HTML file that has not, as yet, been touched.</p>
<p>For reference, I put a copy of the HTML code in figure 89 so you may want to refer back to that, but the important point from that is lines 22 to 24 which are repeated below.</p>
<pre><code>
1. &lt;div id="origin-text"&gt;
2. &lt;p&gt;The text to test.&lt;/p&gt;
3. &lt;/div&gt;&lt;!-- #origin-text --&gt;
</code></pre>
<p>If we want to change the text that the user Is being asked to type, we can amend the paragraph to show some more complex text. Another point worth making is that this is still a very simple test, but there are some things we could do to improve it which might also act as a worthwhile programming exercise. This includes things like calculating the words per minute for the test, showing the number of errors, provide an array of text samples so the application can switch between these to add a bit of variety or add a high-score table.</p>
</article>
<button class="previous" onclick="window.location.href='events.html';">
Previous Chapter - JavaScript and the DOM, Part 2: Events
</button>
<button class="next" onclick="window.location.href='loops.html';">
Next Chapter - Loops
</button>
<button class="coursebutton" onclick="window.location.href='javascriptessentialtraining.html'">
Course Contents
</button>
<button class="webdevbutton" onclick="window.location.href='/webdevelopment/webdevelopment.html'">
Web Development Page
</button>
<button class="homebutton" onclick="window.location.href='/index.html'">
Home
</button>
<br><br>
</body>
</html>