diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2183d69 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "angulardoc.repoId": "a547aa17-a9ab-4f77-ba4c-f3167ae617d2", + "angulardoc.lastSync": 0 +} \ No newline at end of file diff --git a/exercises/03 - CSS Variables/README.md b/exercises/03 - CSS Variables/README.md index 101a76f..7a35a35 100644 --- a/exercises/03 - CSS Variables/README.md +++ b/exercises/03 - CSS Variables/README.md @@ -1,10 +1,11 @@ # Exercise 3: CSS Variables + Nitish Dayal, Software & Applications Developer - [Contact](http://nitishdayal.me) Last Commit Date: May 12, 2017 The web page provided in this exercise displays an image, and has 3 form inputs from which the user can manipulate the padding, blur amount, and background - color of the image. Update the CSS and write the JavaScript code necessary to + color of the image. Update the CSS and write the JavaScript code necessary to bring functionality to the inputs. ## Guide @@ -24,6 +25,7 @@ The purpose of this exercise is to gain experience using _CSS3 variables_. These **Steps:** - CSS: + 1. Declare a new style for the `:root` element and declare three variables inside the style definition for `:root` with the same names as the `input` _HTML elements_. _CSS3 variables_ are declared in the following syntax format: @@ -36,7 +38,8 @@ The purpose of this exercise is to gain experience using _CSS3 variables_. These --padding: 10px; } ``` - 2. Declare a new style for the `img` element and set the `background`, `filter`, and + + 1. Declare a new style for the `img` element and set the `background`, `filter`, and `padding` properties to the variables we defined at the root element: ```CSS /* 'var(--variableName)' to use previously defined CSS properties */ @@ -47,21 +50,26 @@ The purpose of this exercise is to gain experience using _CSS3 variables_. These padding: var(--padding); } ``` - 3. Declare a new style for the `.hl` class and set the color to the `base` variable. + + 1. Declare a new style for the `.hl` class and set the color to the `base` variable. - JavaScript: + 1. Declare & define a variable as a reference to all of the inputs on the page. - 2. Iterate through the _HTML Node Elements_ that the variable is referencing and - attach _event listeners_ to all of them that will call on an _event handler_ whenever - the input value has been changed. - 3. Repeat step 2, listening for mouse movements on the inputs instead of value - changes. - 4. Define a function that will be used as the _event handler_. This will update - the value of the _CSS3 variable_ **at the document level** corresponding with the - `input` element's `name` property which triggered the event handler. - - Minor 'gotcha': Properties like `padding` and `blur` won't update because + + 1. Iterate through the _HTML Node Elements_ that the variable is referencing and + attach _event listeners_ to each one that will call on an _event handler_ whenever + the input value has been changed (the `change` event). + + 1. Repeat step 2, listening for mouse movements on the inputs instead of value + changes (the `mousemove` event). + + 1. Define a function that will be used as the _event handler_. It will update + the value of the _CSS3 variable_ **at the root document level** corresponding with + the `name` property of the `input` element which called this function. + - Minor 'gotcha': Properties like `padding` and `blur` won't update because the value from the input does not include the type of measurement we are using - ('px', 'em', etc.). The `input` _HTML elements_ also have a `data-*` property if + ('px', 'em', etc.). The `input` _HTML elements_ also have a `data-sizing` property if they require a suffix. We can use this to attach the correct suffix to the value if necessary. diff --git a/exercises/03 - CSS Variables/index.html b/exercises/03 - CSS Variables/index.html index b21e1ab..d422ba0 100644 --- a/exercises/03 - CSS Variables/index.html +++ b/exercises/03 - CSS Variables/index.html @@ -1,29 +1,26 @@ - Scoped CSS Variables and JS - -

Update CSS Variables with JS

@@ -64,26 +60,43 @@

Update CSS Variables with JS

- + - - - \ No newline at end of file + diff --git a/exercises/04 - Array Cardio Day 1/index.html b/exercises/04 - Array Cardio Day 1/index.html index 82b4c22..b7bcaf7 100644 --- a/exercises/04 - Array Cardio Day 1/index.html +++ b/exercises/04 - Array Cardio Day 1/index.html @@ -8,6 +8,7 @@ diff --git a/exercises/05 - Flex Panel Gallery/index.html b/exercises/05 - Flex Panel Gallery/index.html index 7aa0b83..80e4ac1 100644 --- a/exercises/05 - Flex Panel Gallery/index.html +++ b/exercises/05 - Flex Panel Gallery/index.html @@ -1,6 +1,5 @@ - Flex Panels 💪 @@ -27,29 +26,26 @@ .panel1 { background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500); } - + .panel2 { background-image: url(https://source.unsplash.com/1CD3fd8kHnE/1500x1500); } - + .panel3 { background-image: url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); } - + .panel4 { background-image: url(https://source.unsplash.com/ITjiVXcwVng/1500x1500); } - + .panel5 { background-image: url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500); } - - -

Hey

@@ -81,20 +77,18 @@ - - - - - \ No newline at end of file + diff --git a/exercises/06 - Type Ahead/index.html b/exercises/06 - Type Ahead/index.html index a9847cc..5753c51 100644 --- a/exercises/06 - Type Ahead/index.html +++ b/exercises/06 - Type Ahead/index.html @@ -31,10 +31,9 @@ .then(data => cities.push(...data)) // Step 4 - const matchInput = (inputString, cities) => cities.filter((location) => { - const regex = new RegExp(inputString, 'gi'); - return location.city.match(regex) || location.state.match(regex) - }); + const matchInput = (inputString, cities) => cities.filter(({city, state}) => ( + city.match(new RegExp(inputString, 'gi')) || state.match(new RegExp(inputString, 'gi')) + )); // Step 6 const numberWithCommas = (x) => x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); diff --git a/exercises/07 - Array Cardio Day 2/index.html b/exercises/07 - Array Cardio Day 2/index.html index a8ee84c..50e4385 100644 --- a/exercises/07 - Array Cardio Day 2/index.html +++ b/exercises/07 - Array Cardio Day 2/index.html @@ -25,32 +25,33 @@ { text: 'Nice Nice Nice!', id: 542328 } ]; + const yr = new Date().getFullYear(); // Some and Every Checks - // Array.prototype.some() + // Array.prototype.some() // is at least one person 19? - const isAdult = people.some(person => ((new Date()).getFullYear()) - person.year >= 19); + const isAdult = people.some(({year}) => yr - year >= 19); console.log({ isAdult }); // Array.prototype.every() // is everyone 19? - const allAdults = people.every(person => ((new Date()).getFullYear()) - person.year >= 19); + const allAdults = people.every(({year}) => yr - year >= 19); console.log({ allAdults }); // Array.prototype.find() // Find is like filter, but instead returns just the one you are looking for // find the comment with the ID of 823423 - const comment = comments.find(comment => comment.id === 823423); + const comment = comments.find(({id}) => id === 823423); console.log(comment); // Array.prototype.findIndex() // Find the comment with this ID 823423 and delete it - const index = comments.findIndex(comment => comment.id === 823423); + const index = comments.findIndex(({id}) => id === 823423); const newComments = [ ...comments.slice(0, index), ...comments.slice(index + 1) ]; - console.log(newComments) + console.table(newComments) - \ No newline at end of file + diff --git a/exercises/08 - Fun with HTML5 Canvas/index.html b/exercises/08 - Fun with HTML5 Canvas/index.html index d54107b..354589f 100644 --- a/exercises/08 - Fun with HTML5 Canvas/index.html +++ b/exercises/08 - Fun with HTML5 Canvas/index.html @@ -15,6 +15,7 @@ - \ No newline at end of file + diff --git a/exercises/10 - Hold Shift and Check Checkboxes/index.html b/exercises/10 - Hold Shift and Check Checkboxes/index.html index 9b2a3ff..27975cb 100644 --- a/exercises/10 - Hold Shift and Check Checkboxes/index.html +++ b/exercises/10 - Hold Shift and Check Checkboxes/index.html @@ -60,7 +60,7 @@
@@ -105,43 +105,52 @@ - \ No newline at end of file + diff --git a/exercises/11 - Custom Video Player/scripts.js b/exercises/11 - Custom Video Player/scripts.js index 5f66391..8ff9f58 100644 --- a/exercises/11 - Custom Video Player/scripts.js +++ b/exercises/11 - Custom Video Player/scripts.js @@ -1,36 +1,50 @@ +"use strict"; (() => { - const player = document.querySelector('.player'), - video = player.querySelector('.viewer'), - progress = player.querySelector('.progress'), - progressBar = player.querySelector('.progress__filled'), - toggle = player.querySelector('.toggle'), - skipButtons = player.querySelectorAll('[data-skip]'), - ranges = player.querySelectorAll('.player__slider') + const player = document.querySelector(".player"), + video = player.querySelector(".viewer"), + progress = player.querySelector(".progress"), + progressBar = player.querySelector(".progress__filled"), + toggle = player.querySelector(".toggle"), + skipButtons = player.querySelectorAll("[data-skip]"), + ranges = player.querySelectorAll(".player__slider"); - let togglePlay = () => video[video.paused ? 'play' : 'pause'](), - updateButton = () => toggle.textContent = video.paused ? '►' : '❚ ❚', - handleProgress = () => progressBar.style.flexBasis = `${(video.currentTime / video.duration) * 100}%`, - scrub = (e) => video.currentTime = ((e.offsetX / progress.offsetWidth) * video.duration) + const togglePlay = () => video[video.paused ? "play" : "pause"](), + updateButton = () => toggle.textContent = video.paused ? "►" : "❚ ❚", + handleProgress = () => + progressBar.style.flexBasis = `${video.currentTime / video.duration * 100}%`, + scrub = e => + video.currentTime = e.offsetX / progress.offsetWidth * video.duration, + progressMoved = e => mousedown && scrub(e), + progressUp = e => mousedown = false, + progressDown = e => mousedown = true, + bClick = b => video.currentTime += parseFloat(b.dataset.skip), + updateRange = (range, e) => video[range.name] = range.value; - video.addEventListener('click', togglePlay) - video.addEventListener('play', updateButton) - video.addEventListener('pause', updateButton) - video.addEventListener('timeupdate', handleProgress) + let mousedown = false; - toggle.addEventListener('click', togglePlay) + const events = [ + { event: "click", handler: togglePlay, target: video }, + { event: "play", handler: updateButton, target: video }, + { event: "pause", handler: updateButton, target: video }, + { event: "timeupdate", handler: handleProgress, target: video }, + { event: "click", handler: togglePlay, target: toggle }, + { event: "click", handler: scrub, target: progress }, + { event: "mousedown", handler: progressDown, target: progress }, + { event: "mousemove", handler: progressMoved, target: progress }, + { event: "mouseup", handler: progressUp, target: progress }, + { event: "click", handler: bClick, target: skipButtons }, + { event: ["change", "mousemove"], handler: updateRange, target: ranges } + ]; - let mousedown = false - progress.addEventListener('click', scrub) - progress.addEventListener('mousemove', (e) => mousedown && scrub(e)) - progress.addEventListener('mousedown', () => mousedown = true) - progress.addEventListener('mouseup', () => mousedown = false) - - skipButtons.forEach(button => { - button.addEventListener('click', () => video.currentTime += parseFloat(button.dataset.skip)) - }) - - ranges.forEach(range => { - range.addEventListener('change', () => video[range.name] = range.value) - range.addEventListener('mousemove', () => video[range.name] = range.value) - }) + events.forEach( + ({ event: e, handler: h, target: t }) => + (t instanceof NodeList + ? t.forEach((el, i) => { + el.addEventListener( + typeof e === "string" ? e : e[i], + typeof h === "function" ? h.bind(null, el) : h[i].bind(null, el) + ); + }) + : t.addEventListener(e, h)) + ); })(); diff --git a/exercises/12 - Key Sequence Detection/index.html b/exercises/12 - Key Sequence Detection/index.html index e9d7a0b..1165a62 100644 --- a/exercises/12 - Key Sequence Detection/index.html +++ b/exercises/12 - Key Sequence Detection/index.html @@ -11,7 +11,8 @@ - \ No newline at end of file + diff --git a/exercises/13 - Slide in on Scroll/index.html b/exercises/13 - Slide in on Scroll/index.html index c872b99..61daf57 100644 --- a/exercises/13 - Slide in on Scroll/index.html +++ b/exercises/13 - Slide in on Scroll/index.html @@ -139,18 +139,20 @@

Slide in on Scroll

(() => { const sliderImages = document.querySelectorAll('.slide-in'); - function debounce(func, wait = 20, immediate = true) { + const debounce = (func, wait = 20, immediate = true) => { let timeout; - return function () { - const context = this, args = arguments; - const later = function () { + + return (...args)=> { + const later = () => { timeout = null; - if (!immediate) func.apply(context, args); + if (!immediate) func.apply(this, args); }; + const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); + + if (callNow) func.apply(this, args); }; }; @@ -229,4 +231,4 @@

Slide in on Scroll

- \ No newline at end of file + diff --git a/exercises/17 - Sort Without Articles/index.html b/exercises/17 - Sort Without Articles/index.html index 7274fce..dc54ee2 100644 --- a/exercises/17 - Sort Without Articles/index.html +++ b/exercises/17 - Sort Without Articles/index.html @@ -77,4 +77,4 @@ - \ No newline at end of file + diff --git a/exercises/18 - Adding Up Times with Reduce/index.html b/exercises/18 - Adding Up Times with Reduce/index.html index 6af08d8..673e96c 100644 --- a/exercises/18 - Adding Up Times with Reduce/index.html +++ b/exercises/18 - Adding Up Times with Reduce/index.html @@ -186,7 +186,7 @@ - \ No newline at end of file + diff --git a/exercises/19 - Webcam Fun/scripts.js b/exercises/19 - Webcam Fun/scripts.js index 9fb2230..2ceeed6 100644 --- a/exercises/19 - Webcam Fun/scripts.js +++ b/exercises/19 - Webcam Fun/scripts.js @@ -18,7 +18,7 @@ }; const paintToCanavas = () => { - const width = video.videoWidth, height = video.videoHeight; + const { videoWidth: width, videoHeight: height } = video; canvas.width = width; canvas.height = height; diff --git a/exercises/20 - Speech Detection/index.html b/exercises/20 - Speech Detection/index.html index 86b329b..457ab15 100644 --- a/exercises/20 - Speech Detection/index.html +++ b/exercises/20 - Speech Detection/index.html @@ -23,7 +23,7 @@ recognition.addEventListener('result', e => { const transcript = Array.from(e.results) .map(results => results[0]) - .map(result => result.transcript) + .map(({trascript}) => transcript) .join('') newParagraphElement.textContent = transcript @@ -31,7 +31,7 @@ newParagraphElement = document.createElement('p') words.appendChild(newParagraphElement) } - + }) recognition.addEventListener('end', recognition.start) diff --git a/exercises/21 - Geolocation/index-START.html b/exercises/21 - Geolocation/index-START.html index e3c4677..f51d895 100644 --- a/exercises/21 - Geolocation/index-START.html +++ b/exercises/21 - Geolocation/index-START.html @@ -61,8 +61,10 @@

const speed = document.querySelector('.speed-value'); navigator.geolocation.watchPosition((data) => { - console.info(data); - speed.textContent = data.coords.speed; + console.log(data); + + const { speed: dSpeed, heading } = data.coords; + speed.textContent = dSpeed; arrow.style.transform = `rotate(${data.coords.heading}deg)`; }, (err) => { console.err(err); diff --git a/readme.md b/readme.md index 78bede1..ff699a0 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@ Nitish Dayal, Software & Applications Developer Last Commit Date: May 12, 2017 -> Course created by [Wes Bos](https://github.com/wesbos) +> Course created by [Wes Bos](https://github.com/wesbos) > Join the challenge (for free!) here - [JavaScript30](https://javascript30.com/account) This repository contains my written guides for the JavaScript30 course by @@ -22,7 +22,7 @@ This repository contains my written guides for the JavaScript30 course by ## About -Build 30 things in 30 days with vanilla JavaScript; no frameworks, libraries, etc. +Build 30 things in 30 days with vanilla JavaScript; no frameworks, libraries, etc. Pacing is totally up to the individual; if you feel like knocking out 30 challenges in 30 minutes, hey, more power to you, but that would miss the point of this course (IMO). The idea behind these exercises is to utilize small amounts of what would regularly be @@ -36,41 +36,41 @@ I think it's fair to say that, coming into this course, you should have a decent a challenge, but given that these exercises require you to work with those very topics time and time again, JavaScript30 is still an excellent learning resource. -The starter files (available [here](https://github.com/wesbos/JavaScript30)) include solutions to - most challenges, so this isn't really meant to be taken as some kind of competition. - JavaScript30 is focused more on helping developers enhance their current skillset and - reducing developer reliance on external JS libraries; **if it can be done with a JS library, +The starter files (available [here](https://github.com/wesbos/JavaScript30)) include solutions to + most challenges, so this isn't really meant to be taken as some kind of competition. + JavaScript30 is focused more on helping developers enhance their current skillset and + reducing developer reliance on external JS libraries; **if it can be done with a JS library, it can (probably) be done with vanilla JS.** ## Table Of Contents -1. [JavaScript Drum Kit](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/01%20-%20JavaScript%20Drum%20Kit) -2. [JS + CSS Clock](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/02%20-%20JS%20%2B%20CSS%20Clock) -3. [CSS Variables](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/03%20-%20CSS%20Variables) -4. [Array Cardio, Day 1](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/04%20-%20Array%20Cardio%20Day%201/) -5. [Flex Panel Gallery](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/05%20-%20Flex%20Panel%20Gallery/) -6. [Type Ahead](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/06%20-%20Type%20Ahead/) -7. [Array Cardio, Day 2](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/07%20-%20Array%20Cardio%20Day%202/) -8. [Fun with HTML5 Canvas](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/08%20-%20Fun%20with%20HTML5%20Canvas/) -9. [Dev Tools Domination](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/09%20-%20DevTools%20Domination/) -10. [Hold Shift and Check Checkboxes](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/10%20-%20Hold%20Shift%20and%20Check%20Checkboxes/) -11. [Custom Video Player](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/11%20-%20Custom%20Video%20Player/) -12. [Key Sequence Detection](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/12%20-%20Key%20Sequence%20Detection/) -13. [Slide in on Scroll](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/13%20-%20Slide%20in%20on%20Scroll/) -14. [JavaScript References vs. Copying](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/14%20-%20JavaScript%20References%20VS%20Copying) -15. [LocalStorage](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/15%20-%20LocalStorage/) -16. [Mouse Move Shadow](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/16%20-%20Mouse%20Move%20Shadow/) -17. [Sort Without Articles](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/17%20-%20Sort%20Without%20Articles/) -18. [Adding Up Times with Reduce](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/18%20-%20Adding%20Up%20Times%20with%20Reduce/) -19. [Webcam Fun](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/19%20-%20Webcam%20Fun/) -20. [Speech Detection](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/20%20-%20Speech%20Detection/) -21. [Geolocation](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/21%20-%20Geolocation/) -22. [Follow Along Link Highlighter](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/22%20-%20Follow%20Along%20Link%20Highlighter/) -23. [Speech Synthesis](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/23%20-%20Speech%20Synthesis/) -24. [Sticky Nav](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/24%20-%20Sticky%20Nav/) -25. [Event Capture, Propagation, Bubbling, and Once](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/25%20-%20Event%20Capture,%20Propagation,%20Bubbling%20and%20Once/) -26. [Stripe Follow Along Nav](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/26%20-%20Stripe%20Follow%20Along%20Nav/) -27. [Click and Drag](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/27%20-%20Click%20and%20Drag/) -28. [Video Speed Controller](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/28%20-%20Video%20Speed%20Controller/) -29. [Countdown Timer](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/29%20-%20Countdown%20Timer/) -30. [Whack A Mole](https://github.com/nitishdayal/JavaScript30/tree/master/exercises/30%20-%20Whack%20A%20Mole/) +1. [JavaScript Drum Kit](/exercises/01%20-%20JavaScript%20Drum%20Kit) +2. [JS + CSS Clock](/exercises/02%20-%20JS%20%2B%20CSS%20Clock) +3. [CSS Variables](/exercises/03%20-%20CSS%20Variables) +4. [Array Cardio, Day 1](/exercises/04%20-%20Array%20Cardio%20Day%201/) +5. [Flex Panel Gallery](/exercises/05%20-%20Flex%20Panel%20Gallery/) +6. [Type Ahead](/exercises/06%20-%20Type%20Ahead/) +7. [Array Cardio, Day 2](/exercises/07%20-%20Array%20Cardio%20Day%202/) +8. [Fun with HTML5 Canvas](/exercises/08%20-%20Fun%20with%20HTML5%20Canvas/) +9. [Dev Tools Domination](/exercises/09%20-%20DevTools%20Domination/) +10. [Hold Shift and Check Checkboxes](/exercises/10%20-%20Hold%20Shift%20and%20Check%20Checkboxes/) +11. [Custom Video Player](/exercises/11%20-%20Custom%20Video%20Player/) +12. [Key Sequence Detection](/exercises/12%20-%20Key%20Sequence%20Detection/) +13. [Slide in on Scroll](/exercises/13%20-%20Slide%20in%20on%20Scroll/) +14. [JavaScript References vs. Copying](/exercises/14%20-%20JavaScript%20References%20VS%20Copying) +15. [LocalStorage](/exercises/15%20-%20LocalStorage/) +16. [Mouse Move Shadow](/exercises/16%20-%20Mouse%20Move%20Shadow/) +17. [Sort Without Articles](/exercises/17%20-%20Sort%20Without%20Articles/) +18. [Adding Up Times with Reduce](/exercises/18%20-%20Adding%20Up%20Times%20with%20Reduce/) +19. [Webcam Fun](/exercises/19%20-%20Webcam%20Fun/) +20. [Speech Detection](/exercises/20%20-%20Speech%20Detection/) +21. [Geolocation](/exercises/21%20-%20Geolocation/) +22. [Follow Along Link Highlighter](/exercises/22%20-%20Follow%20Along%20Link%20Highlighter/) +23. [Speech Synthesis](/exercises/23%20-%20Speech%20Synthesis/) +24. [Sticky Nav](/exercises/24%20-%20Sticky%20Nav/) +25. [Event Capture, Propagation, Bubbling, and Once](/exercises/25%20-%20Event%20Capture,%20Propagation,%20Bubbling%20and%20Once/) +26. [Stripe Follow Along Nav](/exercises/26%20-%20Stripe%20Follow%20Along%20Nav/) +27. [Click and Drag](/exercises/27%20-%20Click%20and%20Drag/) +28. [Video Speed Controller](/exercises/28%20-%20Video%20Speed%20Controller/) +29. [Countdown Timer](/exercises/29%20-%20Countdown%20Timer/) +30. [Whack A Mole](/exercises/30%20-%20Whack%20A%20Mole/)