diff --git a/4-typing-game/images/demo.gif b/4-typing-game/images/demo.gif index 171388ee..d5216457 100644 Binary files a/4-typing-game/images/demo.gif and b/4-typing-game/images/demo.gif differ diff --git a/4-typing-game/solution/index.css b/4-typing-game/solution/index.css index c1767359..90e49919 100644 --- a/4-typing-game/solution/index.css +++ b/4-typing-game/solution/index.css @@ -1,8 +1,90 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + color: #fff; + font-family: "Barlow Condensed"; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + background: linear-gradient(to top, #141e30, #243b55); +} + +main { + padding: 1.2rem; + border-radius: 5px; + display: flex; + flex-direction: column; + gap: 0.5rem; + text-align: center; +} + +input { + font-size: 1.6rem; + display: block; + width: 100%; + height: 1.8rem; + color: #fff; + border: none; + background-color: #243b55; + border-radius: 5px; +} + +h1 { + font-family: "Bungee Spice", sans-serif; + font-size: 3.2rem; +} + +.title { + font-weight: 700; + font-size: 1.2rem; + color: #ccc; + margin-bottom: 1.2rem; +} + +button { + width: 100%; + font-family: "Barlow Condensed"; + font-weight: 700; + font-size: 1.2rem; + letter-spacing: 0.4rem; + text-transform: uppercase; + height: 2.2rem; + background: linear-gradient(to top, #f12711, #f5af19); + color: #fff; + transition: all 300ms; + border: none; +} + +button:hover, +button:active { + background: linear-gradient(to top, #fc4a1a, #f7b733); + color: #000; +} +.win { + font-family: "Barlow Condensed"; + display: flex; + justify-content: center; + align-items: center; + background-color: #1fae37; + height: 0; + transition: all 300ms; +} +#quote { + font-family: "Barlow Condensed"; + font-size: 2rem; +} + .highlight { - background-color: yellow; + background-color: #f5af19; + transition: all 500ms; } .error { - background-color: lightcoral; - border-color: red; + background-color: lightcoral; + border-color: red; } diff --git a/4-typing-game/solution/index.html b/4-typing-game/solution/index.html index a4742715..9bc436a3 100644 --- a/4-typing-game/solution/index.html +++ b/4-typing-game/solution/index.html @@ -1,23 +1,38 @@ - - - Typing - - + + + + + + + + Typing + + - -

Practice your typing

-
Click start to have a quote displayed. Type the quote as fast as you can!
+ +
+

Practice your typing

+
+ Click start to have a quote displayed. Type the quote as fast as you + can! +
-

-

-
- -
-
- -
- - +

+
+

+
+
+ +
+
+ +
+ +
+ diff --git a/4-typing-game/solution/index.js b/4-typing-game/solution/index.js index 646c38a3..557b4c1e 100644 --- a/4-typing-game/solution/index.js +++ b/4-typing-game/solution/index.js @@ -1,11 +1,11 @@ const quotes = [ - 'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.', - 'There is nothing more deceptive than an obvious fact.', - 'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.', - 'I never make exceptions. An exception disproves the rule.', - 'What one man can invent another can discover.', - 'Nothing clears up a case so much as stating it to another person.', - 'Education never ends, Watson. It is a series of lessons, with the greatest for the last.', + "When you have eliminated the impossible, whatever remains, however improbable, must be the truth.", + "There is nothing more deceptive than an obvious fact.", + "I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.", + "I never make exceptions. An exception disproves the rule.", + "What one man can invent another can discover.", + "Nothing clears up a case so much as stating it to another person.", + "Education never ends, Watson. It is a series of lessons, with the greatest for the last.", ]; // array for storing the words of the current challenge @@ -16,70 +16,77 @@ let wordIndex = 0; let startTime = Date.now(); // grab UI items -const quoteElement = document.getElementById('quote'); -const messageElement = document.getElementById('message') -const typedValueElement = document.getElementById('typed-value'); +const quoteElement = document.getElementById("quote"); +const messageElement = document.getElementById("message"); +const typedValueElement = document.getElementById("typed-value"); +const winElement = document.querySelector(".win"); -document.getElementById('start').addEventListener('click', function () { - // get a quote - const quoteIndex = Math.floor(Math.random() * quotes.length); - const quote = quotes[quoteIndex]; - // Put the quote into an array of words - words = quote.split(' '); - // reset the word index for tracking - wordIndex = 0; +document.getElementById("start").addEventListener("click", function () { + // get a quote + const quoteIndex = Math.floor(Math.random() * quotes.length); + const quote = quotes[quoteIndex]; + // Put the quote into an array of words + words = quote.split(" "); + // reset the word index for tracking + wordIndex = 0; - // UI updates - // Create an array of span elements so we can set a class - const spanWords = words.map(function(word) { return `${word} `}); - // Convert into string and set as innerHTML on quote display - quoteElement.innerHTML = spanWords.join(''); - // Highlight the first word - quoteElement.childNodes[0].className = 'highlight'; - // Clear any prior messages - messageElement.innerText = ''; + // UI updates + // Create an array of span elements so we can set a class + const spanWords = words.map(function (word) { + return `${word} `; + }); + // Convert into string and set as innerHTML on quote display + quoteElement.innerHTML = spanWords.join(""); + // Highlight the first word + quoteElement.childNodes[0].className = "highlight"; + // Clear any prior messages + winElement.style.height = "0"; + messageElement.innerText = ""; - // Setup the textbox - // Clear the textbox - typedValueElement.value = ''; - // set focus - typedValueElement.focus(); - // set the event handler + // Setup the textbox + // Clear the textbox + typedValueElement.value = ""; + // set focus + typedValueElement.focus(); + // set the event handler - // Start the timer - startTime = new Date().getTime(); + // Start the timer + startTime = new Date().getTime(); }); -typedValueElement.addEventListener('input', (e) => { - // Get the current word - const currentWord = words[wordIndex]; - // get the current value - const typedValue = typedValueElement.value; +typedValueElement.addEventListener("input", (e) => { + // Get the current word + const currentWord = words[wordIndex]; + // get the current value + const typedValue = typedValueElement.value; - if (typedValue === currentWord && wordIndex === words.length - 1) { - // end of quote - // Display success - const elapsedTime = new Date().getTime() - startTime; - const message = `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.`; - messageElement.innerText = message; - } else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) { - // end of word - // clear the typedValueElement for the new word - typedValueElement.value = ''; - // move to the next word - wordIndex++; - // reset the class name for all elements in quote - for (const wordElement of quoteElement.childNodes) { - wordElement.className = ''; - } - // highlight the new word - quoteElement.childNodes[wordIndex].className = 'highlight'; - } else if (currentWord.startsWith(typedValue)) { - // currently correct - // highlight the next word - typedValueElement.className = ''; - } else { - // error state - typedValueElement.className = 'error'; - } + if (typedValue === currentWord && wordIndex === words.length - 1) { + // end of quote + // Display success + const elapsedTime = new Date().getTime() - startTime; + winElement.style.height = "2rem"; + const message = `CONGRATULATIONS! You finished in ${ + elapsedTime / 1000 + } seconds.`; + messageElement.innerText = message; + } else if (typedValue.endsWith(" ") && typedValue.trim() === currentWord) { + // end of word + // clear the typedValueElement for the new word + typedValueElement.value = ""; + // move to the next word + wordIndex++; + // reset the class name for all elements in quote + for (const wordElement of quoteElement.childNodes) { + wordElement.className = ""; + } + // highlight the new word + quoteElement.childNodes[wordIndex].className = "highlight"; + } else if (currentWord.startsWith(typedValue)) { + // currently correct + // highlight the next word + typedValueElement.className = ""; + } else { + // error state + typedValueElement.className = "error"; + } }); diff --git a/4-typing-game/typing-game/README.md b/4-typing-game/typing-game/README.md index 0930c068..184290f7 100644 --- a/4-typing-game/typing-game/README.md +++ b/4-typing-game/typing-game/README.md @@ -77,22 +77,41 @@ Create a new file named **index.html**. Add the following HTML: ```html - - Typing game - - - -

Typing game!

-

Practice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!

-

-

-
- - -
- - + + + + + + + Typing + + + + +
+

Practice your typing

+
+ Click start to have a quote displayed. Type the quote as fast as you + can! +
+ +

+
+

+
+
+ +
+
+ +
+ +
+ ``` @@ -119,13 +138,95 @@ Create a new file named **style.css** and add the following syntax. ```css /* inside style.css */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + color: #fff; + font-family: "Barlow Condensed"; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + background: linear-gradient(to top, #141e30, #243b55); +} + +main { + padding: 1.2rem; + border-radius: 5px; + display: flex; + flex-direction: column; + gap: 0.5rem; + text-align: center; +} + +input { + font-size: 1.6rem; + display: block; + width: 100%; + height: 1.8rem; + color: #fff; + border: none; + background-color: #243b55; + border-radius: 5px; +} + +h1 { + font-family: "Bungee Spice", sans-serif; + font-size: 3.2rem; +} + +.title { + font-weight: 700; + font-size: 1.2rem; + color: #ccc; + margin-bottom: 1.2rem; +} + +button { + width: 100%; + font-family: "Barlow Condensed"; + font-weight: 700; + font-size: 1.2rem; + letter-spacing: 0.4rem; + text-transform: uppercase; + height: 2.2rem; + background: linear-gradient(to top, #f12711, #f5af19); + color: #fff; + transition: all 300ms; + border: none; +} + +button:hover, +button:active { + background: linear-gradient(to top, #fc4a1a, #f7b733); + color: #000; +} +.win { + font-family: "Barlow Condensed"; + display: flex; + justify-content: center; + align-items: center; + background-color: #1fae37; + height: 0; + transition: all 300ms; +} +#quote { + font-family: "Barlow Condensed"; + font-size: 2rem; +} + .highlight { - background-color: yellow; + background-color: #f5af19; + transition: all 500ms; } .error { background-color: lightcoral; - border: red; + border-color: red; } ``` @@ -161,26 +262,28 @@ We're also going to want references to the UI elements: - The message (**message**) ```javascript -// inside script.js -// all of our quotes const quotes = [ - 'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.', - 'There is nothing more deceptive than an obvious fact.', - 'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.', - 'I never make exceptions. An exception disproves the rule.', - 'What one man can invent another can discover.', - 'Nothing clears up a case so much as stating it to another person.', - 'Education never ends, Watson. It is a series of lessons, with the greatest for the last.', + "When you have eliminated the impossible, whatever remains, however improbable, must be the truth.", + "There is nothing more deceptive than an obvious fact.", + "I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.", + "I never make exceptions. An exception disproves the rule.", + "What one man can invent another can discover.", + "Nothing clears up a case so much as stating it to another person.", + "Education never ends, Watson. It is a series of lessons, with the greatest for the last.", ]; -// store the list of words and the index of the word the player is currently typing + +// array for storing the words of the current challenge let words = []; +// stores the index of the word the player is currently typing let wordIndex = 0; -// the starting time +// default value for startTime (will be set on start) let startTime = Date.now(); -// page elements -const quoteElement = document.getElementById('quote'); -const messageElement = document.getElementById('message'); -const typedValueElement = document.getElementById('typed-value'); + +// grab UI items +const quoteElement = document.getElementById("quote"); +const messageElement = document.getElementById("message"); +const typedValueElement = document.getElementById("typed-value"); +const winElement = document.querySelector(".win"); ``` ✅ Go ahead and add more quotes to your game @@ -201,28 +304,31 @@ When the user clicks **start**, we need to select a quote, setup the user interf ```javascript // at the end of script.js -document.getElementById('start').addEventListener('click', () => { +document.getElementById("start").addEventListener("click", () => { // get a quote const quoteIndex = Math.floor(Math.random() * quotes.length); const quote = quotes[quoteIndex]; // Put the quote into an array of words - words = quote.split(' '); + words = quote.split(" "); // reset the word index for tracking wordIndex = 0; // UI updates // Create an array of span elements so we can set a class - const spanWords = words.map(function(word) { return `${word} `}); + const spanWords = words.map(function (word) { + return `${word} `; + }); // Convert into string and set as innerHTML on quote display - quoteElement.innerHTML = spanWords.join(''); + quoteElement.innerHTML = spanWords.join(""); // Highlight the first word - quoteElement.childNodes[0].className = 'highlight'; + quoteElement.childNodes[0].className = "highlight"; // Clear any prior messages - messageElement.innerText = ''; + winElement.style.height = "0"; + messageElement.innerText = ""; // Setup the textbox // Clear the textbox - typedValueElement.value = ''; + typedValueElement.value = ""; // set focus typedValueElement.focus(); // set the event handler @@ -244,6 +350,7 @@ Let's break down the code! - `join` the array to create a string which we can use to update the `innerHTML` on `quoteElement` - This will display the quote to the player - Set the `className` of the first `span` element to `highlight` to highlight it as yellow + - Hiding the `win` class by setting height to `0` - Clean the `messageElement` by setting `innerText` to `''` - Setup the textbox - Clear the current `value` on `typedValueElement` @@ -256,7 +363,7 @@ As the player types, an `input` event will be raised. This event listener will c ```javascript // at the end of script.js -typedValueElement.addEventListener('input', () => { +typedValueElement.addEventListener("input", () => { // Get the current word const currentWord = words[wordIndex]; // get the current value @@ -266,27 +373,30 @@ typedValueElement.addEventListener('input', () => { // end of sentence // Display success const elapsedTime = new Date().getTime() - startTime; - const message = `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.`; + winElement.style.height = "2rem"; + const message = `CONGRATULATIONS! You finished in ${ + elapsedTime / 1000 + } seconds.`; messageElement.innerText = message; - } else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) { + } else if (typedValue.endsWith(" ") && typedValue.trim() === currentWord) { // end of word // clear the typedValueElement for the new word - typedValueElement.value = ''; + typedValueElement.value = ""; // move to the next word wordIndex++; // reset the class name for all elements in quote for (const wordElement of quoteElement.childNodes) { - wordElement.className = ''; + wordElement.className = ""; } // highlight the new word - quoteElement.childNodes[wordIndex].className = 'highlight'; + quoteElement.childNodes[wordIndex].className = "highlight"; } else if (currentWord.startsWith(typedValue)) { // currently correct // highlight the next word - typedValueElement.className = ''; + typedValueElement.className = ""; } else { // error state - typedValueElement.className = 'error'; + typedValueElement.className = "error"; } }); ``` @@ -296,6 +406,7 @@ Let's break down the code! We start by grabbing the current word and the value t - Quote is complete, indicated by `typedValue` being equal to `currentWord`, and `wordIndex` being equal to one less than the `length` of `words` - Calculate `elapsedTime` by subtracting `startTime` from the current time - Divide `elapsedTime` by 1,000 to convert from milliseconds to seconds + - The class `win` is given a height of `2rem` to display the `message` - Display a success message - Word is complete, indicated by `typedValue` ending with a space (the end of a word) and `typedValue` being equal to `currentWord` - Set `value` on `typedElement` to be `''` to allow for the next word to be typed