├── 01 - JavaScript Drum Kit ├── index.html ├── index.js ├── sounds │ ├── boom.wav │ ├── clap.wav │ ├── hihat.wav │ ├── kick.wav │ ├── openhat.wav │ ├── ride.wav │ ├── snare.wav │ ├── tink.wav │ └── tom.wav └── style.css ├── 02 - JS and CSS Clock ├── index.html └── index.js ├── 03 - CSS Variables ├── index.html └── index.js ├── 04 - Array Cardio Day 1 └── index.html ├── 05 - Flex Panel Gallery ├── index.html └── index.js ├── 06 - Type Ahead ├── index.html ├── index.js └── style.css ├── 07 - Array Cardio Day 2 └── index.html ├── 08 - Fun with HTML5 Canvas ├── index.html └── index.js ├── 09 - Dev Tools Domination └── index.html ├── 10 - Hold Shift and Check Checkboxes ├── index.html └── index.js ├── 11 - Custom Video Player ├── index.html ├── scripts.js └── style.css ├── 12 - Key Sequence Detection ├── index.html └── index.js ├── 13 - Slide in on Scroll ├── index.html └── index.js ├── 14 - JavaScript References VS Copying └── index.html ├── 15 - LocalStorage ├── index.html ├── index.js └── style.css ├── 16 - Mouse Move Shadow ├── index.html └── index.js ├── 17 - Sort Without Articles ├── index.html └── index.js ├── 18 - Adding Up Times with Reduce ├── index.html └── index.js ├── 19 - Webcam Fun ├── index.html ├── package-lock.json ├── package.json ├── scripts.js └── style.css ├── 20 - Speech Detection ├── index.html ├── index.js ├── package-lock.json └── package.json ├── 21 - Geolocation ├── index.html ├── index.js ├── package-lock.json └── package.json ├── 22 - Follow Along Link Highlighter ├── index.html ├── index.js └── style.css ├── 23 - Speech Synthesis ├── index.html ├── index.js └── style.css ├── 24 - Sticky Nav ├── index.html ├── index.js └── style.css ├── 25 - Event Capture, Propagation, Bubbling and Once ├── index.html └── index.js ├── 26 - Stripe Follow Along Nav ├── index.html └── index.js ├── 27 - Click and Drag ├── index.html ├── index.js └── style.css ├── 28 - Video Speed Controller ├── index.html ├── index.js └── style.css ├── 29 - Countdown Timer ├── index.html ├── scripts.js └── style.css ├── 30 - Whack A Mole ├── dirt.svg ├── index.html ├── index.js ├── mole.svg └── style.css └── README.md /01 - JavaScript Drum Kit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JS Drum Kit 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | A 16 | clap 17 |
18 |
19 | S 20 | hihat 21 |
22 |
23 | D 24 | kick 25 |
26 |
27 | F 28 | openhat 29 |
30 |
31 | G 32 | boom 33 |
34 |
35 | H 36 | ride 37 |
38 |
39 | J 40 | snare 41 |
42 |
43 | K 44 | tom 45 |
46 |
47 | L 48 | tink 49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/index.js: -------------------------------------------------------------------------------- 1 | function removeTransition(e) { 2 | if (e.propertyName !== "transform") { 3 | return; 4 | } 5 | this.classList.remove("playing"); 6 | } 7 | 8 | 9 | function playSound(e) { 10 | const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`); 11 | const key = document.querySelector(`div[data-key="${e.keyCode}"]`); 12 | 13 | key.classList.add("playing"); 14 | 15 | audio.currentTime = 0; 16 | audio.play(); 17 | } 18 | const keys = document.querySelectorAll(".key"); 19 | keys.forEach(key => key.addEventListener("transitionend", removeTransition)); 20 | 21 | window.addEventListener("keydown", playSound); -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/boom.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/boom.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/clap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/clap.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/hihat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/hihat.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/kick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/kick.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/openhat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/openhat.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/ride.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/ride.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/snare.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/snare.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/tink.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/tink.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/sounds/tom.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HcwXd/JavaScript30/36a94d686c715338dd9009d113aab95079dd3167/01 - JavaScript Drum Kit/sounds/tom.wav -------------------------------------------------------------------------------- /01 - JavaScript Drum Kit/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 10px; 3 | background: url(http://i.imgur.com/b9r5sEL.jpg) bottom center; 4 | background-size: cover; 5 | } 6 | body,html { 7 | margin: 0; 8 | padding: 0; 9 | font-family: sans-serif; 10 | } 11 | 12 | .keys { 13 | display: flex; 14 | flex: 1; 15 | min-height: 100vh; 16 | align-items: center; 17 | justify-content: center; 18 | } 19 | 20 | .key { 21 | border: .4rem solid black; 22 | border-radius: .5rem; 23 | margin: 1rem; 24 | font-size: 1.5rem; 25 | padding: 1rem .5rem; 26 | transition: all .07s ease; 27 | width: 10rem; 28 | text-align: center; 29 | color: white; 30 | background: rgba(0,0,0,0.4); 31 | text-shadow: 0 0 .5rem black; 32 | } 33 | 34 | .playing { 35 | transform: scale(1.1); 36 | border-color: #ffc600; 37 | box-shadow: 0 0 1rem #ffc600; 38 | } 39 | 40 | kbd { 41 | display: block; 42 | font-size: 4rem; 43 | } 44 | 45 | .sound { 46 | font-size: 1.2rem; 47 | text-transform: uppercase; 48 | letter-spacing: .1rem; 49 | color: #ffc600; 50 | } 51 | -------------------------------------------------------------------------------- /02 - JS and CSS Clock/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JS + CSS Clock 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 | 21 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /02 - JS and CSS Clock/index.js: -------------------------------------------------------------------------------- 1 | const secondHand = document.querySelector('.second-hand'); 2 | const minHand = document.querySelector('.min-hand'); 3 | const hourHand = document.querySelector('.hour-hand'); 4 | 5 | function setDate() { 6 | const now = new Date(); 7 | 8 | const seconds = now.getSeconds(); 9 | const secondDegree = (seconds / 60) * 360 + 90; //rotate starts from 12 o'clock 10 | secondHand.style.transform = `rotate(${secondDegree}deg)`; 11 | 12 | const mins = now.getMinutes(); 13 | const minDegree = (mins / 60) * 360 + 90; 14 | minHand.style.transform = `rotate(${minDegree}deg)`; 15 | 16 | const hours = now.getHours(); 17 | const hourDegree = (hours / 60) * 360 + 90; 18 | hourHand.style.transform = `rotate(${hourDegree}deg)`; 19 | } 20 | setInterval(setDate, 1000); 21 | -------------------------------------------------------------------------------- /03 - CSS Variables/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Scoped CSS Variables and JS 7 | 8 | 9 | 10 |

Update CSS Variables with 11 | JS 12 |

13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /03 - CSS Variables/index.js: -------------------------------------------------------------------------------- 1 | const inputs = document.querySelectorAll(".controls input"); 2 | 3 | function updateCSS() { 4 | const suffix = this.dataset.sizing || ""; //dataset gets attribute with prefix data- 5 | document.documentElement.style.setProperty(`--${this.name}`, this.value + suffix); 6 | } 7 | 8 | 9 | inputs.forEach(input => input.addEventListener("change", updateCSS)); 10 | inputs.forEach(input => input.addEventListener("mousemove", updateCSS)); -------------------------------------------------------------------------------- /04 - Array Cardio Day 1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Array Practice 💪 7 | 8 | 9 | 10 |

11 | Psst: have a look at the JavaScript Console 💁

12 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /05 - Flex Panel Gallery/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Flex Panels 💪 7 | 8 | 9 | 10 | 11 | 127 | 128 | 129 |
130 |
131 |

Hey

132 |

Let's

133 |

Dance

134 |
135 |
136 |

Give

137 |

Take

138 |

Receive

139 |
140 |
141 |

Experience

142 |

It

143 |

Today

144 |
145 |
146 |

Give

147 |

All

148 |

You can

149 |
150 |
151 |

Life

152 |

In

153 |

Motion

154 |
155 |
156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /05 - Flex Panel Gallery/index.js: -------------------------------------------------------------------------------- 1 | const panels = document.querySelectorAll('.panel'); 2 | 3 | function toggleOpen() { 4 | this.classList.toggle('open'); 5 | } 6 | 7 | function toggleActive(e) { 8 | console.log(e.propertyName); 9 | if (e.propertyName.includes('flex')) { 10 | this.classList.toggle('open-active'); 11 | } 12 | } 13 | 14 | panels.forEach((panel) => panel.addEventListener('click', toggleOpen)); 15 | 16 | panels.forEach((panel) => panel.addEventListener('transitionend', toggleActive)); 17 | -------------------------------------------------------------------------------- /06 - Type Ahead/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Type Ahead 👀 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /06 - Type Ahead/index.js: -------------------------------------------------------------------------------- 1 | const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json'; 2 | const cities = []; 3 | 4 | /* 5 | let blob = fetch(endpoint); 6 | console.log(blob); 7 | // Promise {} 8 | 9 | fetch(endpoint).then((blob) => console.log(blob)); 10 | // Response {type: "cors", url: "https://gist.githubusercontent.com/Miserlou/c5cd83…258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json", ...} 11 | */ 12 | fetch(endpoint) 13 | .then((blob) => blob.json()) 14 | .then((data) => cities.push(...data)); 15 | 16 | function findMatches(wordToMatch, cities) { 17 | return cities.filter((place) => { 18 | const regex = new RegExp(wordToMatch, 'gi'); 19 | return place.city.match(regex) || place.state.match(regex); 20 | }); 21 | } 22 | 23 | function numberWithCommas(x) { 24 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); 25 | } 26 | 27 | function displayMatches() { 28 | const matchArray = findMatches(this.value, cities); 29 | const html = matchArray 30 | .map((place) => { 31 | const regex = new RegExp(this.value, 'gi'); 32 | const cityName = place.city.replace(regex, `${this.value}`); 33 | const stateName = place.state.replace(regex, `${this.value}`); 34 | return ` 35 |
  • 36 | ${cityName}, ${stateName} 37 | ${numberWithCommas(place.population)} 38 |
  • 39 | `; 40 | }) 41 | .join(''); 42 | suggestions.innerHTML = html; 43 | } 44 | 45 | const searchInput = document.querySelector('.search'); 46 | const suggestions = document.querySelector('.suggestions'); 47 | 48 | searchInput.addEventListener('change', displayMatches); 49 | searchInput.addEventListener('keyup', displayMatches); 50 | -------------------------------------------------------------------------------- /06 - Type Ahead/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | background:#ffc600; 4 | font-family:'helvetica neue'; 5 | font-size: 20px; 6 | font-weight: 200; 7 | } 8 | *, *:before, *:after { 9 | box-sizing: inherit; 10 | } 11 | input { 12 | width: 100%; 13 | padding:20px; 14 | } 15 | 16 | .search-form { 17 | max-width:400px; 18 | margin:50px auto; 19 | } 20 | 21 | input.search { 22 | margin: 0; 23 | text-align: center; 24 | outline:0; 25 | border: 10px solid #F7F7F7; 26 | width: 120%; 27 | left: -10%; 28 | position: relative; 29 | top: 10px; 30 | z-index: 2; 31 | border-radius: 5px; 32 | font-size: 40px; 33 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.19); 34 | } 35 | 36 | 37 | .suggestions { 38 | margin: 0; 39 | padding: 0; 40 | position: relative; 41 | /*perspective:20px;*/ 42 | } 43 | .suggestions li { 44 | background:white; 45 | list-style: none; 46 | border-bottom: 1px solid #D8D8D8; 47 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.14); 48 | margin:0; 49 | padding:20px; 50 | transition:background 0.2s; 51 | display:flex; 52 | justify-content:space-between; 53 | text-transform: capitalize; 54 | } 55 | 56 | .suggestions li:nth-child(even) { 57 | transform: perspective(100px) rotateX(3deg) translateY(2px) scale(1.001); 58 | background: linear-gradient(to bottom, #ffffff 0%,#EFEFEF 100%); 59 | } 60 | .suggestions li:nth-child(odd) { 61 | transform: perspective(100px) rotateX(-3deg) translateY(3px); 62 | background: linear-gradient(to top, #ffffff 0%,#EFEFEF 100%); 63 | } 64 | 65 | span.population { 66 | font-size: 15px; 67 | } 68 | 69 | .hl { 70 | background:#ffc600; 71 | } -------------------------------------------------------------------------------- /07 - Array Cardio Day 2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Array Cardio 💪💪 7 | 8 | 9 | 10 |

    11 | Psst: have a look at the JavaScript Console 💁

    12 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /08 - Fun with HTML5 Canvas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HTML5 Canvas 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /08 - Fun with HTML5 Canvas/index.js: -------------------------------------------------------------------------------- 1 | const canvas = document.querySelector('#draw'); 2 | const ctx = canvas.getContext('2d'); 3 | 4 | canvas.width = window.innerWidth; 5 | canvas.height = window.innerHeight; 6 | 7 | ctx.strokeStyle = '#BADA55'; 8 | ctx.lineJoin = 'round'; 9 | ctx.lineCap = 'round'; 10 | ctx.lineWidth = 20; 11 | 12 | let isDrawing = false; 13 | let lastX = 0; 14 | let lastY = 0; 15 | 16 | let hue = 0; 17 | let direction = true; 18 | 19 | function draw(e) { 20 | if (!isDrawing) { 21 | return; 22 | } 23 | 24 | ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`; 25 | 26 | ctx.beginPath(); 27 | // start from 28 | ctx.moveTo(lastX, lastY); 29 | // go to 30 | ctx.lineTo(e.offsetX, e.offsetY); 31 | ctx.stroke(); 32 | [lastX, lastY] = [e.offsetX, e.offsetY]; 33 | 34 | hue++; 35 | if (hue >= 360) { 36 | hue = 0; 37 | } 38 | if (ctx.lineWidth >= 50 || ctx.lineWidth <= 1) { 39 | direction = !direction; 40 | } 41 | 42 | if (direction) { 43 | ctx.lineWidth++; 44 | } else { 45 | ctx.lineWidth--; 46 | } 47 | } 48 | 49 | canvas.addEventListener('mousedown', (e) => { 50 | isDrawing = true; 51 | [lastX, lastY] = [e.offsetX, e.offsetY]; 52 | }); 53 | 54 | canvas.addEventListener('mousemove', draw); 55 | canvas.addEventListener('mouseup', () => (isDrawing = false)); 56 | canvas.addEventListener('mouseout', () => (isDrawing = false)); 57 | -------------------------------------------------------------------------------- /09 - Dev Tools Domination/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Console Tricks! 6 | 7 | 8 | 9 |

    ×BREAK×DOWN×

    10 | 11 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /10 - Hold Shift and Check Checkboxes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hold Shift to Check Multiple Checkboxes 7 | 8 | 9 | 10 | 55 | 61 |
    62 |
    63 | 64 |

    This is an inbox layout.

    65 |
    66 |
    67 | 68 |

    Check one item

    69 |
    70 |
    71 | 72 |

    Hold down your Shift key

    73 |
    74 |
    75 | 76 |

    Check a lower item

    77 |
    78 |
    79 | 80 |

    Everything inbetween should also be set to checked

    81 |
    82 |
    83 | 84 |

    Try do it without any libraries

    85 |
    86 |
    87 | 88 |

    Just regular JavaScript

    89 |
    90 |
    91 | 92 |

    Good Luck!

    93 |
    94 |
    95 | 96 |

    Don't forget to tweet your result!

    97 |
    98 |
    99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /10 - Hold Shift and Check Checkboxes/index.js: -------------------------------------------------------------------------------- 1 | let lastChecked; 2 | let isInBetween = false; 3 | 4 | function handleCheck(e) { 5 | if (e.shiftKey && this.checked) { 6 | input_node.forEach((node) => { 7 | if (node === lastChecked || node === this) { 8 | isInBetween = !isInBetween; 9 | } 10 | if (isInBetween) { 11 | node.checked = true; 12 | } 13 | }); 14 | } 15 | 16 | lastChecked = this; 17 | } 18 | 19 | const input_node = document.querySelectorAll('input'); 20 | input_node.forEach((node) => { 21 | node.addEventListener('click', handleCheck); 22 | }); 23 | -------------------------------------------------------------------------------- /11 - Custom Video Player/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HTML Video Player 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 14 | 15 |
    16 |
    17 |
    18 |
    19 | 20 | 21 | 22 | 23 | 24 |
    25 |
    26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /11 - Custom Video Player/scripts.js: -------------------------------------------------------------------------------- 1 | // Get elements 2 | const player = document.querySelector('.player'); 3 | const video = player.querySelector('.viewer'); 4 | const progress = player.querySelector('.progress'); 5 | const progressBar = player.querySelector('.progress__filled'); 6 | const toggle = player.querySelector('.toggle'); 7 | const skipButtons = player.querySelectorAll('[data-skip]'); 8 | const ranges = player.querySelectorAll('.player__slider'); 9 | 10 | // Build functions 11 | function togglePlayVideo() { 12 | const method = video.paused ? 'play' : 'pause'; 13 | video[method](); 14 | } 15 | 16 | function togglePlayBtn() { 17 | const btnIcon = video.paused ? '►' : '❚ ❚'; 18 | toggle.textContent = btnIcon; 19 | } 20 | 21 | function skipVideo() { 22 | video.currentTime += parseFloat(this.dataset.skip); 23 | } 24 | 25 | function updateInputChange() { 26 | video[this.name] = this.value; 27 | } 28 | 29 | function updateVideoTime() { 30 | const percent = (video.currentTime / video.duration) * 100; 31 | progressBar.style.flexBasis = `${percent}%`; 32 | } 33 | 34 | function jumpByDrag(e) { 35 | const jumpTime = (e.offsetX / progress.offsetWidth) * video.duration; 36 | video.currentTime = jumpTime; 37 | } 38 | 39 | // AddEventListener 40 | video.addEventListener('click', togglePlayVideo); 41 | video.addEventListener('pause', togglePlayBtn); 42 | video.addEventListener('play', togglePlayBtn); 43 | video.addEventListener('timeupdate', updateVideoTime); 44 | 45 | let isMoving = false; 46 | progress.addEventListener('mousemove', (e) => { 47 | isMoving && jumpByDrag(e); 48 | }); 49 | progress.addEventListener('click', jumpByDrag); 50 | progress.addEventListener('mousedown', () => (isMoving = true)); 51 | progress.addEventListener('mouseup', () => (isMoving = false)); 52 | 53 | toggle.addEventListener('click', togglePlayVideo); 54 | skipButtons.forEach((btn) => btn.addEventListener('click', skipVideo)); 55 | ranges.forEach((range) => { 56 | range.addEventListener('change', updateInputChange); 57 | }); 58 | -------------------------------------------------------------------------------- /11 - Custom Video Player/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | 5 | *, 6 | *:before, 7 | *:after { 8 | box-sizing: inherit; 9 | } 10 | 11 | body { 12 | margin: 0; 13 | padding: 0; 14 | display: flex; 15 | background: #7a419b; 16 | min-height: 100vh; 17 | background: linear-gradient(135deg, #7c1599 0%, #921099 48%, #7e4ae8 100%); 18 | background-size: cover; 19 | align-items: center; 20 | justify-content: center; 21 | } 22 | 23 | .player { 24 | max-width: 750px; 25 | border: 5px solid rgba(0, 0, 0, 0.2); 26 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); 27 | position: relative; 28 | font-size: 0; 29 | overflow: hidden; 30 | } 31 | 32 | /* This css is only applied when fullscreen is active. */ 33 | .player:fullscreen { 34 | max-width: none; 35 | width: 100%; 36 | } 37 | 38 | .player:-webkit-full-screen { 39 | max-width: none; 40 | width: 100%; 41 | } 42 | 43 | .player__video { 44 | width: 100%; 45 | } 46 | 47 | .player__button { 48 | background: none; 49 | border: 0; 50 | line-height: 1; 51 | color: white; 52 | text-align: center; 53 | outline: 0; 54 | padding: 0; 55 | cursor: pointer; 56 | max-width: 50px; 57 | } 58 | 59 | .player__button:focus { 60 | border-color: #ffc600; 61 | } 62 | 63 | .player__slider { 64 | width: 10px; 65 | height: 30px; 66 | } 67 | 68 | .player__controls { 69 | display: flex; 70 | position: absolute; 71 | bottom: 0; 72 | width: 100%; 73 | transform: translateY(100%) translateY(-5px); 74 | transition: all 0.3s; 75 | flex-wrap: wrap; 76 | background: rgba(0, 0, 0, 0.1); 77 | } 78 | 79 | .player:hover .player__controls { 80 | transform: translateY(0); 81 | } 82 | 83 | .player:hover .progress { 84 | height: 15px; 85 | } 86 | 87 | .player__controls > * { 88 | flex: 1; 89 | } 90 | 91 | .progress { 92 | flex: 10; 93 | position: relative; 94 | display: flex; 95 | flex-basis: 100%; 96 | height: 5px; 97 | transition: height 0.3s; 98 | background: rgba(0, 0, 0, 0.5); 99 | cursor: ew-resize; 100 | } 101 | 102 | .progress__filled { 103 | width: 50%; 104 | background: #ffc600; 105 | flex: 0; 106 | flex-basis: 0%; 107 | } 108 | 109 | /* unholy css to style input type="range" */ 110 | 111 | input[type='range'] { 112 | -webkit-appearance: none; 113 | background: transparent; 114 | width: 100%; 115 | margin: 0 5px; 116 | } 117 | input[type='range']:focus { 118 | outline: none; 119 | } 120 | input[type='range']::-webkit-slider-runnable-track { 121 | width: 100%; 122 | height: 8.4px; 123 | cursor: pointer; 124 | box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0); 125 | background: rgba(255, 255, 255, 0.8); 126 | border-radius: 1.3px; 127 | border: 0.2px solid rgba(1, 1, 1, 0); 128 | } 129 | input[type='range']::-webkit-slider-thumb { 130 | height: 15px; 131 | width: 15px; 132 | border-radius: 50px; 133 | background: #ffc600; 134 | cursor: pointer; 135 | -webkit-appearance: none; 136 | margin-top: -3.5px; 137 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.2); 138 | } 139 | input[type='range']:focus::-webkit-slider-runnable-track { 140 | background: #bada55; 141 | } 142 | input[type='range']::-moz-range-track { 143 | width: 100%; 144 | height: 8.4px; 145 | cursor: pointer; 146 | box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0); 147 | background: #ffffff; 148 | border-radius: 1.3px; 149 | border: 0.2px solid rgba(1, 1, 1, 0); 150 | } 151 | input[type='range']::-moz-range-thumb { 152 | box-shadow: 0 0 0 rgba(0, 0, 0, 0), 0 0 0 rgba(13, 13, 13, 0); 153 | height: 15px; 154 | width: 15px; 155 | border-radius: 50px; 156 | background: #ffc600; 157 | cursor: pointer; 158 | } 159 | -------------------------------------------------------------------------------- /12 - Key Sequence Detection/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Key Detection 7 | 8 | 9 | 10 | 11 |

    Do not type fuck!

    12 |

    Type ↑↓ ← → to start a brand new life

    13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /12 - Key Sequence Detection/index.js: -------------------------------------------------------------------------------- 1 | let secret = 'fuck'; 2 | let clean = 'ArrowUpArrowDownArrowLeftArrowRight'; 3 | let press_record = []; 4 | window.addEventListener('keyup', (e) => { 5 | console.log(e.key); 6 | press_record.push(e.key); 7 | press_record.splice(-secret.length - 1, press_record.length - secret.length); 8 | if (press_record.join('').includes(secret)) { 9 | cornify_add(); 10 | } 11 | if (press_record.join('').includes(clean)) { 12 | document.body.innerHTML = ''; 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /13 - Slide in on Scroll/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 | 11 |
    12 | 13 |

    Slide in on Scroll

    14 | 15 |

    Consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero ipsum delectus quidem 16 | dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum nam! Fuga ad tempora 17 | in aspernatur pariaturlores sunt esse magni, ut, dignissimos.

    18 |

    Lorem ipsum cupiditate, corporis a qui libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut 19 | asperiores omnis blanditiis quod quas laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt 20 | esse magni, ut, dignissimos.

    21 |

    Adipisicing elit. Tempore tempora rerum..

    22 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 23 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 24 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    25 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 26 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 27 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    28 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 29 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 30 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    31 | 32 | 33 | 34 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptates, deserunt facilis et iste corrupti omnis tenetur 35 | est. Iste ut est dicta dolor itaque adipisci, dolorum minima, veritatis earum provident error molestias. Ratione magni 36 | illo sint vel velit ut excepturi consectetur suscipit, earum modi accusamus voluptatem nostrum, praesentium numquam, 37 | reiciendis voluptas sit id quisquam. Consequatur in quis reprehenderit modi perspiciatis necessitatibus saepe, quidem, 38 | suscipit iure natus dignissimos ipsam, eligendi deleniti accusantium, rerum quibusdam fugit perferendis et optio recusandae 39 | sed ratione. Culpa, dolorum reprehenderit harum ab voluptas fuga, nisi eligendi natus maiores illum quas quos et aperiam 40 | aut doloremque optio maxime fugiat doloribus. Eum dolorum expedita quam, nesciunt

    41 | 42 | 43 | 44 |

    at provident praesentium atque quas rerum optio dignissimos repudiandae ullam illum quibusdam. Vel ad error quibusdam, 45 | illo ex totam placeat. Quos excepturi fuga, molestiae ea quisquam minus, ratione dicta consectetur officia omnis, doloribus 46 | voluptatibus? Veniam ipsum veritatis architecto, provident quas consequatur doloremque quam quidem earum expedita, 47 | ad delectus voluptatum, omnis praesentium nostrum qui aspernatur ea eaque adipisci et cumque ab? Ea voluptatum dolore 48 | itaque odio. Eius minima distinctio harum, officia ab nihil exercitationem. Tempora rem nemo nam temporibus molestias 49 | facilis minus ipsam quam doloribus consequatur debitis nesciunt tempore officiis aperiam quisquam, molestiae voluptates 50 | cum, fuga culpa. Distinctio accusamus quibusdam, tempore perspiciatis dolorum optio facere consequatur quidem ullam 51 | beatae architecto, ipsam sequi officiis dignissimos amet impedit natus necessitatibus tenetur repellendus dolor rem! 52 | Dicta dolorem, iure, facilis illo ex nihil ipsa amet officia, optio temporibus eum autem odit repellendus nisi. Possimus 53 | modi, corrupti error debitis doloribus dicta libero earum, sequi porro ut excepturi nostrum ea voluptatem nihil culpa? 54 | Ullam expedita eligendi obcaecati reiciendis velit provident omnis quas qui in corrupti est dolore facere ad hic, animi 55 | soluta assumenda consequuntur reprehenderit! Voluptate dolor nihil veniam laborum voluptas nisi pariatur sed optio 56 | accusantium quam consectetur, corrupti, sequi et consequuntur, excepturi doloremque. Tempore quis velit corporis neque 57 | fugit non sequi eaque rem hic. Facere, inventore, aspernatur. Accusantium modi atque, asperiores qui nobis soluta cumque 58 | suscipit excepturi possimus doloremque odit saepe perferendis temporibus molestiae nostrum voluptatum quis id sint 59 | quidem nesciunt culpa. Rerum labore dolor beatae blanditiis praesentium explicabo velit optio esse aperiam similique, 60 | voluptatem cum, maiores ipsa tempore. Reiciendis sed culpa atque inventore, nam ullam enim expedita consectetur id 61 | velit iusto alias vitae explicabo nemo neque odio reprehenderit soluta sint eaque. Aperiam, qui ut tenetur, voluptate 62 | doloremque officiis dicta quaerat voluptatem rerum natus magni. Eum amet autem dolor ullam.

    63 | 64 | 65 | 66 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero 67 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero 68 | perferendis, deserunt et incidunt eveniet 69 | temporibus doloremque possimus facilis. Possimus labore, officia dolore! Eaque ratione saepe, alias harum laboriosam 70 | deserunt laudantium blanditiis eum explicabo placeat reiciendis labore iste sint. Consectetur expedita dignissimos, 71 | non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, rerum ratione consequatur, culpa consectetur 72 | possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod doloremque dignissimos necessitatibus 73 | aliquid laboriosam architecto at cupiditate commodi expedita in, quae blanditiis. Deserunt labore sequi, repellat laboriosam 74 | est, doloremque culpa reiciendis tempore excepturi. Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui 75 | rem quam error sint, libero. Laboriosam rem, ratione. Autem blanditiis

    76 | 77 | 78 |

    laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit 79 | ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda natus cupiditate hic 80 | quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos dolore culpa 81 | debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro saepe iure sunt 82 | eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, ratione eveniet, 83 | provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus quia facilis 84 | iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores quis necessitatibus 85 | distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, quasi aspernatur quas 86 | odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, odit excepturi voluptate 87 | saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore non magnam, amet, facere 88 | ducimus accusantium eos veritatis neque.

    89 | 90 | 91 | 92 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero 93 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero 94 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! 95 | Eaque ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore 96 | iste sint. Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, 97 | rerum ratione consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel 98 | non incidunt, quod doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita 99 | in, quae blanditiis. Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. 100 | Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. 101 | Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, 102 | laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda 103 | natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos 104 | dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro 105 | saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, 106 | ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus 107 | quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores 108 | quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, 109 | quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, 110 | odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore 111 | non magnam, amet, facere ducimus accusantium eos veritatis neque.

    112 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero 113 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero 114 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! 115 | Eaque ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore 116 | iste sint. Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, 117 | rerum ratione consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel 118 | non incidunt, quod doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita 119 | in, quae blanditiis. Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. 120 | Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. 121 | Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, 122 | laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda 123 | natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos 124 | dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro 125 | saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, 126 | ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus 127 | quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores 128 | quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, 129 | quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, 130 | odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore 131 | non magnam, amet, facere ducimus accusantium eos veritatis neque.

    132 | 133 | 134 | 135 | 136 |
    137 | 138 | 139 | 140 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /13 - Slide in on Scroll/index.js: -------------------------------------------------------------------------------- 1 | function debounce(func, wait = 20, immediate = true) { 2 | var timeout; 3 | return function() { 4 | var context = this, 5 | args = arguments; 6 | var later = function() { 7 | timeout = null; 8 | if (!immediate) func.apply(context, args); 9 | }; 10 | var callNow = immediate && !timeout; 11 | clearTimeout(timeout); 12 | timeout = setTimeout(later, wait); 13 | if (callNow) func.apply(context, args); 14 | }; 15 | } 16 | 17 | const sliderImages = document.querySelectorAll('.slide-in'); 18 | 19 | function checkScroll() { 20 | sliderImages.forEach((img) => { 21 | let scrollDistanceToImgMiddle = window.scrollY + window.innerHeight - img.height / 2; 22 | let distanceToImgBottom = img.offsetTop + img.height; 23 | let isScrollToMiddle = scrollDistanceToImgMiddle > img.offsetTop; 24 | let isNotScrollOverBottom = window.scrollY < distanceToImgBottom; 25 | if (isScrollToMiddle && isNotScrollOverBottom) { 26 | img.classList.add('active'); 27 | } else { 28 | img.classList.remove('active'); 29 | } 30 | }); 31 | } 32 | 33 | window.addEventListener('scroll', debounce(checkScroll)); 34 | -------------------------------------------------------------------------------- /14 - JavaScript References VS Copying/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS Reference VS Copy 6 | 7 | 8 | 9 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /15 - LocalStorage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LocalStorage 7 | 8 | 9 | 10 | 11 | 15 | 16 | 18 | 19 | 21 | 23 | 25 | 27 | 29 | 30 | 31 | 32 | 33 |
    34 |

    LOCAL TAPAS

    35 |

    36 | 39 |
    40 | 41 | 42 |
    43 |
    44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /15 - LocalStorage/index.js: -------------------------------------------------------------------------------- 1 | const addItems = document.querySelector('.add-items'); 2 | const itemsList = document.querySelector('.plates'); 3 | const items = JSON.parse(localStorage.getItem('items')) || []; 4 | 5 | function addItem(e) { 6 | e.preventDefault(); 7 | const inputText = this.querySelector('[name=item]').value; 8 | const item = { 9 | inputText, 10 | done: false, 11 | }; 12 | items.push(item); 13 | 14 | renderItems(items, itemsList); 15 | localStorage.setItem('items', JSON.stringify(items)); 16 | this.reset(); 17 | } 18 | 19 | function renderItems(plates = [], plateList) { 20 | plateList.innerHTML = plates 21 | .map((plate, i) => { 22 | return ` 23 |
  • 24 | 25 | 26 |
  • 27 | `; 28 | }) 29 | .join(''); 30 | } 31 | 32 | function toggleDone(e) { 33 | console.log('aa'); 34 | 35 | if (!e.target.matches('input')) return; 36 | 37 | const el = e.target; 38 | const index = el.dataset.index; 39 | items[index].done = !items[index].done; 40 | localStorage.setItem('items', JSON.stringify(items)); 41 | renderItems(items, itemsList); 42 | } 43 | 44 | addItems.addEventListener('submit', addItem); 45 | itemsList.addEventListener('click', toggleDone); 46 | renderItems(items, itemsList); 47 | -------------------------------------------------------------------------------- /15 - LocalStorage/style.css: -------------------------------------------------------------------------------- 1 | 2 | html { 3 | box-sizing: border-box; 4 | background:url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat; 5 | background-size:cover; 6 | min-height:100vh; 7 | display:flex; 8 | justify-content: center; 9 | align-items: center; 10 | text-align: center; 11 | font-family: Futura,"Trebuchet MS",Arial,sans-serif 12 | } 13 | *, *:before, *:after {box-sizing: inherit; } 14 | 15 | svg { 16 | fill:white; 17 | background: rgba(0,0,0,0.1); 18 | padding: 20px; 19 | border-radius: 50%; 20 | width:200px; 21 | margin-bottom: 50px; 22 | } 23 | 24 | .wrapper { 25 | padding: 20px; 26 | max-width: 350px; 27 | background: rgba(255,255,255,0.95); 28 | box-shadow: 0 0 0 10px rgba(0,0,0,0.1); 29 | } 30 | 31 | h2 { 32 | text-align: center; 33 | margin: 0; 34 | font-weight: 200; 35 | } 36 | 37 | .plates { 38 | margin: 0; 39 | padding: 0; 40 | text-align: left; 41 | list-style: none; 42 | } 43 | 44 | .plates li { 45 | border-bottom: 1px solid rgba(0,0,0,0.2); 46 | padding: 10px 0; 47 | font-weight: 100; 48 | display: flex; 49 | } 50 | 51 | .plates label { 52 | flex:1; 53 | cursor: pointer; 54 | 55 | } 56 | 57 | .plates input { 58 | display: none; 59 | } 60 | 61 | .plates input + label:before { 62 | content: '⬜️'; 63 | margin-right: 10px; 64 | } 65 | 66 | .plates input:checked + label:before { 67 | content: '🌮'; 68 | } 69 | 70 | .add-items { 71 | margin-top: 20px; 72 | } 73 | 74 | .add-items input { 75 | padding:10px; 76 | outline:0; 77 | border:1px solid rgba(0,0,0,0.1); 78 | } 79 | -------------------------------------------------------------------------------- /16 - Mouse Move Shadow/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mouse Shadow 7 | 8 | 9 | 10 | 11 |
    12 |

    🔥WOAH!

    13 |
    14 | 15 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /16 - Mouse Move Shadow/index.js: -------------------------------------------------------------------------------- 1 | const wrap = document.querySelector('.hero'); 2 | const text = document.querySelector('h1'); 3 | const shadowBasis = 500; 4 | 5 | function renderShadow(e) { 6 | const { offsetWidth: wrapWidth, offsetHeight: wrapHeight } = wrap; 7 | let { offsetX: x, offsetY: y } = e; 8 | 9 | if (e.target !== this) { 10 | x += e.target.offsetLeft; 11 | y += e.target.offsetTop; 12 | } 13 | 14 | let xShadow = Math.round((x / wrapWidth) * shadowBasis - shadowBasis / 2); 15 | let yShadow = Math.round((y / wrapHeight) * shadowBasis - shadowBasis / 2); 16 | 17 | text.style.textShadow = ` 18 | ${xShadow}px ${yShadow}px 0 rgba(0, 255, 0 , 0.7), 19 | ${-xShadow}px ${yShadow}px 0 rgba(255, 0, 0 , 0.7), 20 | ${xShadow}px ${-yShadow}px 0 rgba(0, 0, 255, 0.7), 21 | ${-xShadow}px ${-yShadow}px 0 rgba(0, 255, 255 , 0.7), 22 | ${xShadow * 2}px ${yShadow * 2}px 10px rgba(0, 255, 0 , 0.7), 23 | ${-xShadow * 2}px ${yShadow * 2}px 10px rgba(255, 0, 0 , 0.7), 24 | ${xShadow * 2}px ${-yShadow * 2}px 10px rgba(0, 0, 255, 0.7), 25 | ${-xShadow * 2}px ${-yShadow * 2}px 10px rgba(0, 255, 255 , 0.7) 26 | `; 27 | } 28 | 29 | wrap.addEventListener('mousemove', renderShadow); 30 | -------------------------------------------------------------------------------- /17 - Sort Without Articles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sort Without Articles 7 | 8 | 9 | 10 | 11 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /17 - Sort Without Articles/index.js: -------------------------------------------------------------------------------- 1 | const bands = [ 2 | 'The Plot in You', 3 | 'The Devil Wears Prada', 4 | 'Pierce the Veil', 5 | 'Norma Jean', 6 | 'The Bled', 7 | 'Say Anything', 8 | 'The Midway State', 9 | 'We Came as Romans', 10 | 'Counterparts', 11 | 'Oh, Sleeper', 12 | 'A Skylit Drive', 13 | 'Anywhere But Here', 14 | 'An Old Dog', 15 | ]; 16 | 17 | function stripExtraWord(name) { 18 | return name.replace(/^(a |the |an )/i, '').trim(); 19 | } 20 | 21 | const sorterdBand = bands.sort((a, b) => (stripExtraWord(a) > stripExtraWord(b) ? 1 : -1)); 22 | 23 | document.querySelector('#bands').innerHTML = sorterdBand 24 | .map((band) => { 25 | return ` 26 |
  • ${band}
  • 27 | `; 28 | }) 29 | .join(''); 30 | -------------------------------------------------------------------------------- /18 - Adding Up Times with Reduce/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Videos 7 | 8 | 9 | 10 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /18 - Adding Up Times with Reduce/index.js: -------------------------------------------------------------------------------- 1 | const vidTimesNodes = [...document.querySelectorAll('[data-time]')]; 2 | 3 | const totalTime = vidTimesNodes 4 | .map((node) => node.dataset.time) 5 | .map((timeCode) => { 6 | const [min, sec] = timeCode.split(':').map(parseFloat); 7 | console.log([min, sec], min * 60 + sec); 8 | return min * 60 + sec; 9 | }) 10 | .reduce((total, vidSecs) => total + vidSecs); 11 | 12 | let secondsLeft = totalTime; 13 | 14 | let hours = Math.floor(secondsLeft / 3600); 15 | secondsLeft = secondsLeft % 3600; 16 | 17 | let mins = Math.floor(secondsLeft / 60); 18 | secondsLeft = secondsLeft % 60; 19 | 20 | console.log(`${hours}:${mins}:${secondsLeft}`); 21 | -------------------------------------------------------------------------------- /19 - Webcam Fun/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Get User Media Code Along! 6 | 7 | 8 | 9 | 10 |
    11 |
    12 | 13 | 33 |
    34 | 35 | 36 | 37 |
    38 |
    39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /19 - Webcam Fun/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gum", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "scripts.js", 6 | "scripts": { 7 | "start": "browser-sync start --server --files \"*.css, *.html, *.js\"" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "browser-sync": "^2.12.5 <2.23.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /19 - Webcam Fun/scripts.js: -------------------------------------------------------------------------------- 1 | const video = document.querySelector('.player'); 2 | const canvas = document.querySelector('.photo'); 3 | const ctx = canvas.getContext('2d'); 4 | const strip = document.querySelector('.strip'); 5 | const snap = document.querySelector('.snap'); 6 | 7 | function getUserVideo() { 8 | navigator.mediaDevices 9 | .getUserMedia({ video: true, audio: false }) 10 | .then((localMediaStream) => { 11 | console.log(localMediaStream); 12 | video.src = window.URL.createObjectURL(localMediaStream); 13 | video.play(); 14 | }) 15 | .catch((err) => { 16 | console.error(`OH NO!!!`, err); 17 | }); 18 | } 19 | 20 | function renderVideoOnCanvas() { 21 | canvas.width = video.videoWidth; 22 | canvas.height = video.videoHeight; 23 | 24 | return setInterval(() => { 25 | ctx.drawImage(video, 0, 0, canvas.width, canvas.height); 26 | 27 | let pixels = ctx.getImageData(0, 0, canvas.width, canvas.height); 28 | // pixels = redEffect(pixels); 29 | 30 | pixels = rgbSplit(pixels); 31 | ctx.globalAlpha = 0.1; 32 | 33 | // pixels = greenScreen(pixels); 34 | 35 | ctx.putImageData(pixels, 0, 0); 36 | }, 16); 37 | } 38 | 39 | function redEffect(pixels) { 40 | for (let i = 0; i < pixels.data.length; i += 4) { 41 | pixels.data[i + 0] = pixels.data[i + 0] + 200; // RED 42 | pixels.data[i + 1] = pixels.data[i + 1] - 50; // GREEN 43 | pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // Blue 44 | } 45 | return pixels; 46 | } 47 | 48 | function rgbSplit(pixels) { 49 | for (let i = 0; i < pixels.data.length; i += 4) { 50 | pixels.data[i - 150] = pixels.data[i + 0]; // RED 51 | pixels.data[i + 500] = pixels.data[i + 1]; // GREEN 52 | pixels.data[i - 550] = pixels.data[i + 2]; // Blue 53 | } 54 | return pixels; 55 | } 56 | 57 | function greenScreen(pixels) { 58 | const levels = {}; 59 | 60 | document.querySelectorAll('.rgb input').forEach((input) => { 61 | levels[input.name] = input.value; 62 | }); 63 | 64 | for (i = 0; i < pixels.data.length; i = i + 4) { 65 | red = pixels.data[i + 0]; 66 | green = pixels.data[i + 1]; 67 | blue = pixels.data[i + 2]; 68 | alpha = pixels.data[i + 3]; 69 | 70 | if (red >= levels.rmin && green >= levels.gmin && blue >= levels.bmin && red <= levels.rmax && green <= levels.gmax && blue <= levels.bmax) { 71 | // take it out! 72 | pixels.data[i + 3] = 0; 73 | } 74 | } 75 | 76 | return pixels; 77 | } 78 | 79 | function takePhoto() { 80 | snap.currentTime = 0; 81 | snap.play(); 82 | 83 | const data = canvas.toDataURL('image/jpeg'); 84 | const link = document.createElement('a'); 85 | link.href = data; 86 | link.setAttribute('download', 'screenshot'); 87 | link.innerHTML = `Your Screenshot`; 88 | strip.insertBefore(link, strip.firsChild); 89 | } 90 | getUserVideo(); 91 | 92 | video.addEventListener('canplay', renderVideoOnCanvas); 93 | -------------------------------------------------------------------------------- /19 - Webcam Fun/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | 5 | *, *:before, *:after { 6 | box-sizing: inherit; 7 | } 8 | 9 | html { 10 | font-size: 10px; 11 | background:#ffc600; 12 | } 13 | 14 | .photobooth { 15 | background:white; 16 | max-width:150rem; 17 | margin: 2rem auto; 18 | border-radius:2px; 19 | } 20 | 21 | /*clearfix*/ 22 | .photobooth:after { 23 | content: ''; 24 | display: block; 25 | clear: both; 26 | } 27 | 28 | .photo { 29 | width:100%; 30 | float:left; 31 | } 32 | 33 | .player { 34 | position: absolute; 35 | top:20px; 36 | right: 20px; 37 | width:200px; 38 | } 39 | 40 | /* 41 | Strip! 42 | */ 43 | 44 | .strip { 45 | padding:2rem; 46 | } 47 | .strip img { 48 | width:100px; 49 | overflow-x: scroll; 50 | padding:0.8rem 0.8rem 2.5rem 0.8rem; 51 | box-shadow:0 0 3px rgba(0,0,0,0.2); 52 | background:white; 53 | } 54 | 55 | .strip a:nth-child(5n+1) img { transform: rotate(10deg); } 56 | .strip a:nth-child(5n+2) img { transform: rotate(-2deg); } 57 | .strip a:nth-child(5n+3) img { transform: rotate(8deg); } 58 | .strip a:nth-child(5n+4) img { transform: rotate(-11deg); } 59 | .strip a:nth-child(5n+5) img { transform: rotate(12deg); } 60 | -------------------------------------------------------------------------------- /20 - Speech Detection/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Speech Detection 7 | 8 | 9 | 10 | 11 |
    12 |
    13 | 14 | 15 | 16 | 17 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /20 - Speech Detection/index.js: -------------------------------------------------------------------------------- 1 | window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; 2 | 3 | const recognition = new SpeechRecognition(); 4 | 5 | recognition.interimResults = true; 6 | recognition.lang = 'en-US'; 7 | 8 | let p = document.createElement('p'); 9 | const words = document.querySelector('.words'); 10 | words.appendChild(p); 11 | 12 | recognition.addEventListener('result', (e) => { 13 | // console.log(e); 14 | 15 | // console.log(e.results[0]); 16 | let transcript = [...e.results] 17 | .map((result) => result[0]) 18 | .map((result) => result.transcript) 19 | .join(''); 20 | 21 | if (transcript.includes('***')) { 22 | alert('bad word alert!'); 23 | } 24 | if (transcript.includes('happy')) { 25 | alert('glad to hear that you are happy!'); 26 | } 27 | p.innerText = transcript; 28 | 29 | if (e.results[0].isFinal) { 30 | p = document.createElement('p'); 31 | const words = document.querySelector('.words'); 32 | words.appendChild(p); 33 | } 34 | }); 35 | 36 | recognition.addEventListener('end', recognition.start); 37 | 38 | recognition.start(); 39 | -------------------------------------------------------------------------------- /20 - Speech Detection/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gum", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "scripts.js", 6 | "scripts": { 7 | "start": "browser-sync start --directory --server --files \"*.css, *.html, *.js\"" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "browser-sync": "^2.12.5 <2.23.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /21 - Geolocation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 | 11 | 13 | 14 | 16 | 17 | 19 | 21 | 23 | 25 | 27 | 28 | 29 | 30 | 31 | 32 |

    33 | 0 34 | KM/H 35 |

    36 | 37 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /21 - Geolocation/index.js: -------------------------------------------------------------------------------- 1 | const arrow = document.querySelector('.arrow'); 2 | const speed = document.querySelector('.speed-value'); 3 | 4 | navigator.geolocation.watchPosition( 5 | (data) => { 6 | speed.textContent = data.coords.speed; 7 | arrow.style.transform = `rotate(${data.coords.heading}deg)`; 8 | }, 9 | (err) => { 10 | console.error(err); 11 | } 12 | ); 13 | -------------------------------------------------------------------------------- /21 - Geolocation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gum", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "scripts.js", 6 | "scripts": { 7 | "start": "browser-sync start --directory --server --files \"*.css, *.html, *.js\" --https" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "browser-sync": "^2.12.5 <2.23.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /22 - Follow Along Link Highlighter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 👀👀👀Follow Along Nav 7 | 8 | 9 | 10 | 11 | 12 | 31 | 32 |
    33 |

    Lorem ipsum dolor sit amet, 34 | consectetur adipisicing elit. Est 35 | explicabo unde natus necessitatibus esse obcaecati distinctio, aut itaque, qui vitae!

    36 |

    Aspernatur sapiente quae sint 37 | soluta modi, atque praesentium laborum pariatur earum 38 | quaerat cupiditate consequuntur facilis ullam dignissimos, aperiam quam veniam.

    39 |

    Cum ipsam quod, incidunt sit ex 40 | tempore placeat maxime 41 | corrupti possimus 42 | veritatis ipsum fugit recusandae est doloremque? Hic, 43 | quibusdam, nulla.

    44 |

    Esse quibusdam, ad, ducimus cupiditate 45 | nulla, quae magni odit 46 | totam ut consequatur eveniet sunt quam provident sapiente dicta neque quod.

    47 |

    Aliquam 48 | dicta sequi culpa fugiat 49 | consequuntur pariatur optio ad minima, maxime 50 | odio, distinctio magni impedit tempore enim repellendus 51 | repudiandae quas!

    52 |
    53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /22 - Follow Along Link Highlighter/index.js: -------------------------------------------------------------------------------- 1 | const triggers = document.querySelectorAll('a'); 2 | const highlight = document.createElement('span'); 3 | highlight.className = 'highlight'; 4 | document.body.appendChild(highlight); 5 | 6 | function showHighlight() { 7 | let DOMRect = this.getBoundingClientRect(); 8 | let cord = { 9 | width: DOMRect.width, 10 | height: DOMRect.height, 11 | top: DOMRect.top + window.scrollY, 12 | left: DOMRect.left + window.screenX, 13 | }; 14 | highlight.style.width = `${cord.width + 8}px`; 15 | highlight.style.height = `${cord.height}px`; 16 | 17 | highlight.style.transform = `translate(${cord.left - 4}px, ${cord.top}px)`; 18 | } 19 | 20 | triggers.forEach((link) => link.addEventListener('mouseenter', showHighlight)); 21 | -------------------------------------------------------------------------------- /22 - Follow Along Link Highlighter/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | *, 5 | *:before, 6 | *:after { 7 | box-sizing: inherit; 8 | } 9 | body { 10 | min-height: 100vh; 11 | margin: 0; /* Important! */ 12 | font-family: sans-serif; 13 | background: linear-gradient(45deg, hsla(340, 100%, 55%, 1) 0%, hsla(340, 100%, 55%, 0) 70%), linear-gradient(135deg, hsla(225, 95%, 50%, 1) 10%, hsla(225, 95%, 50%, 0) 80%), 14 | linear-gradient(225deg, hsla(140, 90%, 50%, 1) 10%, hsla(140, 90%, 50%, 0) 80%), linear-gradient(315deg, hsla(35, 95%, 55%, 1) 100%, hsla(35, 95%, 55%, 0) 70%); 15 | } 16 | 17 | .wrapper { 18 | margin: 0 auto; 19 | max-width: 500px; 20 | font-size: 20px; 21 | line-height: 2; 22 | position: relative; 23 | } 24 | 25 | a { 26 | text-decoration: none; 27 | color: black; 28 | /* background:rgba(0,0,0,0.05); */ 29 | border-radius: 20px; 30 | } 31 | 32 | .highlight { 33 | transition: all 0.2s; 34 | border-bottom: 2px solid white; 35 | position: absolute; 36 | top: 0; 37 | background: white; 38 | left: 0; 39 | z-index: -1; 40 | border-radius: 20px; 41 | display: block; 42 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); 43 | } 44 | 45 | .menu { 46 | padding: 0; 47 | display: flex; 48 | list-style: none; 49 | justify-content: center; 50 | margin: 100px 0; 51 | } 52 | 53 | .menu a { 54 | display: inline-block; 55 | padding: 5px; 56 | margin: 0 20px; 57 | color: black; 58 | } 59 | -------------------------------------------------------------------------------- /23 - Speech Synthesis/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Speech Synthesis 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 | 15 |

    The Voiceinator 5000

    16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
    32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /23 - Speech Synthesis/index.js: -------------------------------------------------------------------------------- 1 | const msg = new SpeechSynthesisUtterance(); 2 | let voicesList = []; 3 | const voicesDropdown = document.querySelector('[name="voice"]'); 4 | const options = document.querySelectorAll('[type="range"], [name="text"]'); 5 | const speakButton = document.querySelector('#speak'); 6 | const stopButton = document.querySelector('#stop'); 7 | 8 | msg.text = document.querySelector('[name="text"]').value; 9 | 10 | function generateVoicesList() { 11 | voicesList = this.getVoices(); 12 | voicesDropdown.innerHTML = voicesList.map((voice) => ``).join(''); 13 | } 14 | 15 | function setVoiceConfig() { 16 | msg.voice = voicesList.find((voice) => voice.name === this.value); 17 | console.log(msg); 18 | togglePlay(); 19 | } 20 | 21 | function togglePlay(startOver = true) { 22 | speechSynthesis.cancel(); 23 | if (startOver) { 24 | speechSynthesis.speak(msg); 25 | } 26 | } 27 | 28 | function handleOptionChange() { 29 | msg[this.name] = this.value; 30 | togglePlay(); 31 | } 32 | 33 | options.forEach((option) => option.addEventListener('change', handleOptionChange)); 34 | voicesDropdown.addEventListener('change', setVoiceConfig); 35 | speechSynthesis.addEventListener('voiceschanged', generateVoicesList); 36 | speakButton.addEventListener('click', togglePlay); 37 | stopButton.addEventListener('click', () => togglePlay(false)); 38 | -------------------------------------------------------------------------------- /23 - Speech Synthesis/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 10px; 3 | box-sizing: border-box; 4 | } 5 | 6 | *, *:before, *:after { 7 | box-sizing: inherit; 8 | } 9 | 10 | body { 11 | margin: 0; 12 | padding: 0; 13 | font-family: sans-serif; 14 | background-color:#3BC1AC; 15 | display:flex; 16 | min-height: 100vh; 17 | align-items: center; 18 | 19 | background-image: 20 | radial-gradient(circle at 100% 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent), 21 | radial-gradient(circle at 0 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent), 22 | radial-gradient(circle at 50% 100%, #42D2BB 10%, #3BC1AC 11%, #3BC1AC 23%, #42D2BB 24%, #42D2BB 30%, #3BC1AC 31%, #3BC1AC 43%, #42D2BB 44%, #42D2BB 50%, #3BC1AC 51%, #3BC1AC 63%, #42D2BB 64%, #42D2BB 71%, transparent 71%, transparent), 23 | radial-gradient(circle at 100% 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent), 24 | radial-gradient(circle at 0 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent); 25 | background-size:100px 50px; 26 | } 27 | 28 | 29 | .voiceinator { 30 | padding:2rem; 31 | width:50rem; 32 | margin:0 auto; 33 | border-radius:1rem; 34 | position: relative; 35 | background:white; 36 | overflow: hidden; 37 | z-index: 1; 38 | box-shadow:0 0 5px 5px rgba(0,0,0,0.1); 39 | } 40 | 41 | h1 { 42 | width:calc(100% + 4rem); 43 | margin: -2rem 0 2rem -2rem; 44 | padding:.5rem; 45 | background: #ffc600; 46 | border-bottom: 5px solid #F3C010; 47 | text-align: center; 48 | font-size: 5rem; 49 | font-weight: 100; 50 | font-family: 'Pacifico', cursive; 51 | text-shadow:3px 3px 0 #F3C010; 52 | 53 | } 54 | 55 | .voiceinator input, 56 | .voiceinator button, 57 | .voiceinator select, 58 | .voiceinator textarea { 59 | width: 100%; 60 | display: block; 61 | margin:10px 0; 62 | padding:10px; 63 | border:0; 64 | font-size: 2rem; 65 | background:#F7F7F7; 66 | outline:0; 67 | } 68 | 69 | textarea { 70 | height: 20rem; 71 | } 72 | 73 | input[type="select"] { 74 | 75 | } 76 | 77 | .voiceinator button { 78 | background:#ffc600; 79 | border:0; 80 | width: 49%; 81 | float:left; 82 | font-family: 'Pacifico', cursive; 83 | margin-bottom: 0; 84 | font-size: 2rem; 85 | border-bottom: 5px solid #F3C010; 86 | cursor:pointer; 87 | position: relative; 88 | } 89 | 90 | .voiceinator button:active { 91 | top:2px; 92 | } 93 | 94 | .voiceinator button:nth-of-type(1) { 95 | margin-right: 2%; 96 | } 97 | -------------------------------------------------------------------------------- /24 - Sticky Nav/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sticky Nav 7 | 8 | 9 | 10 | 11 | 12 |
    13 |

    A story about getting lost.

    14 |
    15 | 16 | 38 | 39 |
    40 | 41 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 42 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 43 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    44 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 45 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 46 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    47 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 48 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 49 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    50 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 51 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 52 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    53 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 54 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 55 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    56 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 57 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 58 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    59 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 60 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 61 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    62 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 63 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 64 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    65 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 66 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 67 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    68 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui 69 | libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas 70 | laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.

    71 | 72 | 73 | 74 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptates, deserunt facilis et iste corrupti omnis tenetur 75 | est. Iste ut est dicta dolor itaque adipisci, dolorum minima, veritatis earum provident error molestias. Ratione magni 76 | illo sint vel velit ut excepturi consectetur suscipit, earum modi accusamus voluptatem nostrum, praesentium numquam, 77 | reiciendis voluptas sit id quisquam. Consequatur in quis reprehenderit modi perspiciatis necessitatibus saepe, quidem, 78 | suscipit iure natus dignissimos ipsam, eligendi deleniti accusantium, rerum quibusdam fugit perferendis et optio recusandae 79 | sed ratione. Culpa, dolorum reprehenderit harum ab voluptas fuga, nisi eligendi natus maiores illum quas quos et aperiam 80 | aut doloremque optio maxime fugiat doloribus. Eum dolorum expedita quam, nesciunt

    81 | 82 | 83 | 84 |

    at provident praesentium atque quas rerum optio dignissimos repudiandae ullam illum quibusdam. Vel ad error quibusdam, 85 | illo ex totam placeat. Quos excepturi fuga, molestiae ea quisquam minus, ratione dicta consectetur officia omnis, doloribus 86 | voluptatibus? Veniam ipsum veritatis architecto, provident quas consequatur doloremque quam quidem earum expedita, 87 | ad delectus voluptatum, omnis praesentium nostrum qui aspernatur ea eaque adipisci et cumque ab? Ea voluptatum dolore 88 | itaque odio. Eius minima distinctio harum, officia ab nihil exercitationem. Tempora rem nemo nam temporibus molestias 89 | facilis minus ipsam quam doloribus consequatur debitis nesciunt tempore officiis aperiam quisquam, molestiae voluptates 90 | cum, fuga culpa. Distinctio accusamus quibusdam, tempore perspiciatis dolorum optio facere consequatur quidem ullam 91 | beatae architecto, ipsam sequi officiis dignissimos amet impedit natus necessitatibus tenetur repellendus dolor rem! 92 | Dicta dolorem, iure, facilis illo ex nihil ipsa amet officia, optio temporibus eum autem odit repellendus nisi. Possimus 93 | modi, corrupti error debitis doloribus dicta libero earum, sequi porro ut excepturi nostrum ea voluptatem nihil culpa? 94 | Ullam expedita eligendi obcaecati reiciendis velit provident omnis quas qui in corrupti est dolore facere ad hic, animi 95 | soluta assumenda consequuntur reprehenderit! Voluptate dolor nihil veniam laborum voluptas nisi pariatur sed optio 96 | accusantium quam consectetur, corrupti, sequi et consequuntur, excepturi doloremque. Tempore quis velit corporis neque 97 | fugit non sequi eaque rem hic. Facere, inventore, aspernatur. Accusantium modi atque, asperiores qui nobis soluta cumque 98 | suscipit excepturi possimus doloremque odit saepe perferendis temporibus molestiae nostrum voluptatum quis id sint 99 | quidem nesciunt culpa. Rerum labore dolor beatae blanditiis praesentium explicabo velit optio esse aperiam similique, 100 | voluptatem cum, maiores ipsa tempore. Reiciendis sed culpa atque inventore, nam ullam enim expedita consectetur id 101 | velit iusto alias vitae explicabo nemo neque odio reprehenderit soluta sint eaque. Aperiam, qui ut tenetur, voluptate 102 | doloremque officiis dicta quaerat voluptatem rerum natus magni. Eum amet autem dolor ullam.

    103 | 104 | 105 | 106 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero 107 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero 108 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! 109 | Eaque ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore 110 | iste sint. Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, 111 | rerum ratione consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel 112 | non incidunt, quod doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita 113 | in, quae blanditiis. Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. 114 | Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. 115 | Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, 116 | laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda 117 | natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos 118 | dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro 119 | saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, 120 | ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus 121 | quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores 122 | quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, 123 | quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, 124 | odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore 125 | non magnam, amet, facere ducimus accusantium eos veritatis neque.

    126 | 127 | 128 | 129 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero 130 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero 131 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! 132 | Eaque ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore 133 | iste sint. Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, 134 | rerum ratione consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel 135 | non incidunt, quod doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita 136 | in, quae blanditiis. Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. 137 | Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. 138 | Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, 139 | laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda 140 | natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos 141 | dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro 142 | saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, 143 | ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus 144 | quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores 145 | quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, 146 | quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, 147 | odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore 148 | non magnam, amet, facere ducimus accusantium eos veritatis neque.

    149 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero 150 | placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero 151 | perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore! 152 | Eaque ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore 153 | iste sint. Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium, 154 | rerum ratione consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel 155 | non incidunt, quod doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita 156 | in, quae blanditiis. Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi. 157 | Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione. 158 | Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, 159 | laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda 160 | natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos 161 | dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro 162 | saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, 163 | ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus 164 | quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores 165 | quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, 166 | quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, 167 | odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore 168 | non magnam, amet, facere ducimus accusantium eos veritatis neque.

    169 |
    170 | 171 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /24 - Sticky Nav/index.js: -------------------------------------------------------------------------------- 1 | const nav = document.querySelector('#main'); 2 | 3 | let navTop = nav.offsetTop; 4 | 5 | function fixNav() { 6 | if (window.scrollY > navTop) { 7 | document.body.style.paddingTop = nav.offsetHeight + 'px'; 8 | document.body.classList.add('fixed-nav'); 9 | } else { 10 | document.body.classList.remove('fixed-nav'); 11 | document.body.style.paddingTop = 0; 12 | } 13 | } 14 | window.addEventListener('scroll', fixNav); 15 | -------------------------------------------------------------------------------- /24 - Sticky Nav/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | background: #eeeeee; 4 | font-family: 'helvetica neue'; 5 | font-size: 20px; 6 | font-weight: 200; 7 | } 8 | body { 9 | margin: 0; 10 | } 11 | *, 12 | *:before, 13 | *:after { 14 | box-sizing: inherit; 15 | } 16 | 17 | .site-wrap { 18 | max-width: 700px; 19 | margin: 70px auto; 20 | background: white; 21 | padding: 40px; 22 | text-align: justify; 23 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05); 24 | transform: scale(0.98); 25 | transition: transform 0.5s; 26 | } 27 | 28 | header { 29 | text-align: center; 30 | height: 50vh; 31 | background: url(http://wes.io/iEgP/wow-so-deep.jpg) bottom center no-repeat; 32 | background-size: cover; 33 | display: flex; 34 | align-items: center; 35 | justify-content: center; 36 | } 37 | 38 | h1 { 39 | color: white; 40 | font-size: 7vw; 41 | text-shadow: 3px 4px 0 rgba(0, 0, 0, 0.2); 42 | } 43 | 44 | nav { 45 | background: black; 46 | top: 0; 47 | width: 100%; 48 | transition: all 0.5s; 49 | position: relative; 50 | z-index: 1; 51 | } 52 | 53 | body.fixed-nav nav { 54 | position: fixed; 55 | box-shadow: 0 5px 0 rgba(0, 0, 0, 0.1); 56 | } 57 | 58 | nav ul { 59 | margin: 0; 60 | padding: 0; 61 | list-style: none; 62 | display: flex; 63 | } 64 | 65 | nav li { 66 | flex: 1; 67 | text-align: center; 68 | display: flex; 69 | justify-content: center; 70 | align-items: center; 71 | } 72 | 73 | li.logo { 74 | max-width: 0; 75 | overflow: hidden; 76 | background: white; 77 | transition: all 0.5s; 78 | font-weight: 600; 79 | font-size: 30px; 80 | } 81 | 82 | li.logo a { 83 | color: black; 84 | } 85 | 86 | .fixed-nav li.logo { 87 | max-width: 500px; 88 | } 89 | 90 | nav a { 91 | text-decoration: none; 92 | padding: 20px; 93 | display: inline-block; 94 | color: white; 95 | transition: all 0.2s; 96 | text-transform: uppercase; 97 | } 98 | -------------------------------------------------------------------------------- /25 - Event Capture, Propagation, Bubbling and Once/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Understanding JavaScript's Capture 7 | 8 | 9 | 10 | 11 |
    12 |
    13 |
    14 |
    15 |
    16 |
    17 | 18 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /25 - Event Capture, Propagation, Bubbling and Once/index.js: -------------------------------------------------------------------------------- 1 | const divs = document.querySelectorAll('div'); 2 | 3 | function logText() { 4 | console.log(this.classList.value); 5 | } 6 | 7 | // Bubble 8 | // divs.forEach((div) => div.addEventListener('click', logText)); 9 | 10 | // Capture 11 | // divs.forEach((div) => div.addEventListener('click', logText, { capture: true })); 12 | 13 | // Once 14 | divs.forEach((div) => div.addEventListener('click', logText, { capture: true, once: true })); 15 | -------------------------------------------------------------------------------- /26 - Stripe Follow Along Nav/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Follow Along Nav 7 | 8 | 9 | 10 |

    Cool

    11 | 87 | 88 | 245 | 246 | 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /26 - Stripe Follow Along Nav/index.js: -------------------------------------------------------------------------------- 1 | const triggers = document.querySelectorAll('.cool > li'); 2 | const background = document.querySelector('.dropdownBackground'); 3 | const nav = document.querySelector('.top'); 4 | 5 | function showWrap() { 6 | this.classList.add('trigger-enter'); 7 | setTimeout(() => { 8 | this.classList.contains('trigger-enter') && this.classList.add('trigger-enter-active'); 9 | }, 150); 10 | const dropdown = this.querySelector('.dropdown'); 11 | const cords = dropdown.getBoundingClientRect(); 12 | const navCords = nav.getBoundingClientRect(); 13 | 14 | background.classList.add('open'); 15 | background.style.width = `${cords.width}px`; 16 | background.style.height = `${cords.height}px`; 17 | background.style.top = `${cords.top - navCords.top}px`; 18 | background.style.left = `${cords.left - navCords.left}px`; 19 | } 20 | 21 | function hideWrap() { 22 | background.classList.remove('open'); 23 | 24 | this.classList.remove('trigger-enter', 'trigger-enter-active'); 25 | } 26 | 27 | triggers.forEach((trigger) => trigger.addEventListener('mouseenter', showWrap)); 28 | triggers.forEach((trigger) => trigger.addEventListener('mouseleave', hideWrap)); 29 | -------------------------------------------------------------------------------- /27 - Click and Drag/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Click and Drag 7 | 8 | 9 | 10 | 11 |
    12 |
    01
    13 |
    02
    14 |
    03
    15 |
    04
    16 |
    05
    17 |
    06
    18 |
    07
    19 |
    08
    20 |
    09
    21 |
    10
    22 |
    11
    23 |
    12
    24 |
    13
    25 |
    14
    26 |
    15
    27 |
    16
    28 |
    17
    29 |
    18
    30 |
    19
    31 |
    20
    32 |
    21
    33 |
    22
    34 |
    23
    35 |
    24
    36 |
    25
    37 |
    38 | 39 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /27 - Click and Drag/index.js: -------------------------------------------------------------------------------- 1 | const slider = document.querySelector('.items'); 2 | 3 | let isMouseDown = false; 4 | let mouseDownX; 5 | let sliderScrollX; 6 | 7 | slider.addEventListener('mousedown', (e) => { 8 | isMouseDown = true; 9 | slider.classList.add('active'); 10 | 11 | mouseDownX = e.pageX - slider.offsetLeft; 12 | sliderScrollX = slider.scrollLeft; 13 | }); 14 | 15 | slider.addEventListener('mouseleave', () => { 16 | isMouseDown = false; 17 | slider.classList.remove('active'); 18 | }); 19 | 20 | slider.addEventListener('mouseup', () => { 21 | isMouseDown = false; 22 | slider.classList.remove('active'); 23 | }); 24 | 25 | slider.addEventListener('mousemove', (e) => { 26 | if (!isMouseDown) { 27 | return; 28 | } 29 | let mouseMoveX = e.pageX - slider.offsetLeft; 30 | let moveDistance = (mouseMoveX - mouseDownX) * 1.5; 31 | slider.scrollLeft = sliderScrollX - moveDistance; 32 | }); 33 | -------------------------------------------------------------------------------- /27 - Click and Drag/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | background: url('https://source.unsplash.com/NFs6dRTBgaM/2000x2000') fixed; 4 | background-size: cover; 5 | } 6 | 7 | *, *:before, *:after { 8 | box-sizing: inherit; 9 | } 10 | 11 | body { 12 | min-height: 100vh; 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | font-family: sans-serif; 17 | font-size: 20px; 18 | margin: 0; 19 | } 20 | 21 | .items { 22 | height:800px; 23 | padding: 100px; 24 | width:100%; 25 | border:1px solid white; 26 | overflow-x: scroll; 27 | overflow-y: hidden; 28 | white-space: nowrap; 29 | user-select: none; 30 | cursor: pointer; 31 | transition: all 0.2s; 32 | transform: scale(0.98); 33 | will-change: transform; 34 | position: relative; 35 | background: rgba(255,255,255,0.1); 36 | font-size: 0; 37 | perspective: 500px; 38 | } 39 | 40 | .items.active { 41 | background: rgba(255,255,255,0.3); 42 | cursor: grabbing; 43 | cursor: -webkit-grabbing; 44 | transform: scale(1); 45 | } 46 | 47 | .item { 48 | width:200px; 49 | height: calc(100% - 40px); 50 | display: inline-flex; 51 | align-items: center; 52 | justify-content: center; 53 | font-size: 80px; 54 | font-weight: 100; 55 | color:rgba(0,0,0,0.15); 56 | box-shadow: inset 0 0 0 10px rgba(0,0,0,0.15); 57 | } 58 | 59 | .item:nth-child(9n+1) { background: dodgerblue;} 60 | .item:nth-child(9n+2) { background: goldenrod;} 61 | .item:nth-child(9n+3) { background: paleturquoise;} 62 | .item:nth-child(9n+4) { background: gold;} 63 | .item:nth-child(9n+5) { background: cadetblue;} 64 | .item:nth-child(9n+6) { background: tomato;} 65 | .item:nth-child(9n+7) { background: lightcoral;} 66 | .item:nth-child(9n+8) { background: darkslateblue;} 67 | .item:nth-child(9n+9) { background: rebeccapurple;} 68 | 69 | .item:nth-child(even) { transform: scaleX(1.31) rotateY(40deg); } 70 | .item:nth-child(odd) { transform: scaleX(1.31) rotateY(-40deg); } 71 | -------------------------------------------------------------------------------- /28 - Video Speed Controller/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Video Speed Scrubber 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 14 |
    15 |
    16 |
    17 |
    18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /28 - Video Speed Controller/index.js: -------------------------------------------------------------------------------- 1 | const speed = document.querySelector('.speed'); 2 | const bar = speed.querySelector('.speed-bar'); 3 | const video = document.querySelector('.flex'); 4 | 5 | function handleSpeedChange(e) { 6 | let innerDistance = e.pageY - this.offsetTop; 7 | let innerPercent = innerDistance / this.offsetHeight; 8 | 9 | let height = Math.round(innerPercent * 100) + '%'; 10 | let min = 0.5; 11 | let max = 5; 12 | let speedRate = innerPercent * (max - min) + min; 13 | 14 | bar.style.height = height; 15 | bar.textContent = speedRate.toFixed(2) + 'x'; 16 | 17 | video.playbackRate = speedRate; 18 | } 19 | 20 | speed.addEventListener('mousemove', handleSpeedChange); 21 | -------------------------------------------------------------------------------- /28 - Video Speed Controller/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | display:flex; 4 | justify-content: center; 5 | align-items: center; 6 | min-height: 100vh; 7 | background:#4C4C4C url('https://unsplash.it/1500/900?image=1021'); 8 | background-size:cover; 9 | font-family: sans-serif; 10 | } 11 | .wrapper { 12 | width:850px; 13 | display:flex; 14 | } 15 | video { 16 | box-shadow:0 0 1px 3px rgba(0,0,0,0.1); 17 | } 18 | 19 | .speed { 20 | background:#efefef; 21 | flex:1; 22 | display:flex; 23 | align-items:flex-start; 24 | margin:10px; 25 | border-radius:50px; 26 | box-shadow:0 0 1px 3px rgba(0,0,0,0.1); 27 | overflow: hidden; 28 | } 29 | .speed-bar { 30 | width:100%; 31 | background:linear-gradient(-170deg, #2376ae 0%, #c16ecf 100%); 32 | text-shadow:1px 1px 0 rgba(0,0,0,0.2); 33 | display: flex; 34 | align-items: center; 35 | justify-content: center; 36 | padding:2px; 37 | color:white; 38 | height:16.3%; 39 | } 40 | -------------------------------------------------------------------------------- /29 - Countdown Timer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Countdown Timer 7 | 8 | 9 | 10 | 11 | 12 |
    13 |
    14 | 15 | 16 | 17 | 18 | 19 |
    20 | 21 |
    22 |
    23 |
    24 |

    25 |

    26 |
    27 |
    28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /29 - Countdown Timer/scripts.js: -------------------------------------------------------------------------------- 1 | let countDownTimer; 2 | const leftTimeDisplay = document.querySelector('.display__time-left'); 3 | const endTimeDisplay = document.querySelector('.display__end-time'); 4 | const btn = document.querySelectorAll('[data-time]'); 5 | 6 | function timer(seconds) { 7 | clearInterval(countDownTimer); 8 | 9 | let endTime = Date.now() + seconds * 1000; 10 | displayLeftTime(seconds); 11 | displayEndTime(endTime); 12 | 13 | countDownTimer = setInterval(() => { 14 | let secLeft = Math.round((endTime - Date.now()) / 1000); 15 | displayLeftTime(secLeft); 16 | if (secLeft <= 0) { 17 | clearInterval(countDownTimer); 18 | } 19 | }, 1000); 20 | } 21 | 22 | function displayLeftTime(seconds) { 23 | let min = Math.floor(seconds / 60); 24 | let sec = seconds % 60; 25 | let displayText = `${min}:${('0' + sec).slice(-2)}`; 26 | leftTimeDisplay.textContent = displayText; 27 | document.title = displayText; 28 | } 29 | 30 | function displayEndTime(seconds) { 31 | let endTime = new Date(seconds); 32 | let hour = endTime.getHours(); 33 | let min = endTime.getMinutes(); 34 | let displayText = `Be Back At ${('0' + hour).slice(-2)}:${('0' + min).slice(-2)}`; 35 | endTimeDisplay.textContent = displayText; 36 | } 37 | 38 | function handleBtnClick() { 39 | timer(parseInt(this.dataset.time)); 40 | } 41 | 42 | btn.forEach((btn) => btn.addEventListener('click', handleBtnClick)); 43 | 44 | document.customForm.addEventListener('submit', function(e) { 45 | e.preventDefault(); 46 | const mins = this.minutes.value; 47 | timer(mins * 60); 48 | this.reset(); 49 | }); 50 | -------------------------------------------------------------------------------- /29 - Countdown Timer/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-size: 10px; 4 | background: #8E24AA; 5 | background: linear-gradient(45deg, #42a5f5 0%,#478ed1 50%,#0d47a1 100%); 6 | } 7 | 8 | *, *:before, *:after { 9 | box-sizing: inherit; 10 | } 11 | 12 | body { 13 | margin:0; 14 | text-align: center; 15 | font-family: 'Inconsolata', monospace; 16 | } 17 | 18 | .display__time-left { 19 | font-weight: 100; 20 | font-size: 20rem; 21 | margin: 0; 22 | color:white; 23 | text-shadow:4px 4px 0 rgba(0,0,0,0.05); 24 | } 25 | 26 | .timer { 27 | display:flex; 28 | min-height: 100vh; 29 | flex-direction:column; 30 | } 31 | 32 | .timer__controls { 33 | display: flex; 34 | } 35 | 36 | .timer__controls > * { 37 | flex:1; 38 | } 39 | 40 | .timer__controls form { 41 | flex:1; 42 | display:flex; 43 | } 44 | 45 | .timer__controls input { 46 | flex:1; 47 | border:0; 48 | padding:2rem; 49 | } 50 | 51 | .timer__button { 52 | background:none; 53 | border:0; 54 | cursor: pointer; 55 | color:white; 56 | font-size: 2rem; 57 | text-transform: uppercase; 58 | background:rgba(0,0,0,0.1); 59 | border-bottom:3px solid rgba(0,0,0,0.2); 60 | border-right:1px solid rgba(0,0,0,0.2); 61 | padding:1rem; 62 | font-family: 'Inconsolata', monospace; 63 | } 64 | 65 | .timer__button:hover, 66 | .timer__button:focus { 67 | background:rgba(0,0,0,0.2); 68 | outline:0; 69 | } 70 | 71 | .display { 72 | flex:1; 73 | display:flex; 74 | flex-direction: column; 75 | align-items: center; 76 | justify-content: center; 77 | } 78 | 79 | .display__end-time { 80 | font-size: 4rem; 81 | color:white; 82 | } 83 | -------------------------------------------------------------------------------- /30 - Whack A Mole/dirt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dirt 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /30 - Whack A Mole/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Whack A Mole! 7 | 8 | 9 | 10 | 11 | 12 | 13 |

    Whack-a-mole! 14 | 0 15 |

    16 | 17 | 18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    33 |
    34 |
    35 |
    36 |
    37 |
    38 | 39 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /30 - Whack A Mole/index.js: -------------------------------------------------------------------------------- 1 | const holes = document.querySelectorAll('.hole'); 2 | const scoreBoard = document.querySelector('.score'); 3 | const moles = document.querySelectorAll('.mole'); 4 | 5 | let lastHole; 6 | let timeUp = false; 7 | let score = 0; 8 | let GAMETIME = 30000; 9 | 10 | function randomTime(min, max) { 11 | return Math.round(Math.random() * (max - min) + min); 12 | } 13 | 14 | function randomHole(holes) { 15 | let holeIdx = Math.floor(Math.random() * holes.length); 16 | let hole = holes[holeIdx]; 17 | if (hole === lastHole) { 18 | return randomHole(holes); 19 | } 20 | lastHole = hole; 21 | return hole; 22 | } 23 | 24 | function molePop() { 25 | let popTime = randomTime(300, 800); 26 | let popHole = randomHole(holes); 27 | 28 | popHole.classList.add('up'); 29 | setTimeout(() => { 30 | popHole.classList.remove('up'); 31 | if (!timeUp) molePop(); 32 | }, popTime); 33 | } 34 | 35 | function startGame() { 36 | score = 0; 37 | scoreBoard.textContent = score; 38 | 39 | timeUp = false; 40 | molePop(); 41 | setTimeout(() => { 42 | timeUp = true; 43 | }, GAMETIME); 44 | } 45 | 46 | function hitMole(e) { 47 | if (!e.isTrusted) return; 48 | 49 | this.parentNode.classList.remove('up'); 50 | score++; 51 | scoreBoard.textContent = score; 52 | } 53 | 54 | moles.forEach((mole) => mole.addEventListener('click', hitMole)); 55 | -------------------------------------------------------------------------------- /30 - Whack A Mole/mole.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mole 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /30 - Whack A Mole/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-size: 10px; 4 | background: #ffc600; 5 | } 6 | 7 | *, *:before, *:after { 8 | box-sizing: inherit; 9 | } 10 | 11 | body { 12 | padding: 0; 13 | margin:0; 14 | font-family: 'Amatic SC', cursive; 15 | } 16 | 17 | h1 { 18 | text-align: center; 19 | font-size: 10rem; 20 | line-height:1; 21 | margin-bottom: 0; 22 | } 23 | 24 | .score { 25 | background:rgba(255,255,255,0.2); 26 | padding:0 3rem; 27 | line-height:1; 28 | border-radius:1rem; 29 | } 30 | 31 | .game { 32 | width:600px; 33 | height:400px; 34 | display:flex; 35 | flex-wrap:wrap; 36 | margin:0 auto; 37 | } 38 | 39 | .hole { 40 | flex: 1 0 33.33%; 41 | overflow: hidden; 42 | position: relative; 43 | } 44 | 45 | .hole:after { 46 | display: block; 47 | background: url(dirt.svg) bottom center no-repeat; 48 | background-size:contain; 49 | content:''; 50 | width: 100%; 51 | height:70px; 52 | position: absolute; 53 | z-index: 2; 54 | bottom:-30px; 55 | } 56 | 57 | .mole { 58 | background:url('mole.svg') bottom center no-repeat; 59 | background-size:60%; 60 | position: absolute; 61 | top: 100%; 62 | width: 100%; 63 | height: 100%; 64 | transition:all 0.4s; 65 | } 66 | 67 | .hole.up .mole { 68 | top:0; 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript30 2 | 3 | > 這是 30 個在 30 天內用 JavaScript 寫的網站練習,我也有紀錄平時寫的 HTML/CSS 練習作品在[這個 Repo](https://github.com/HcwXd/HTML-CSS-practice)。關於這個 JavaScript30 的挑戰,我把完成後的心得與一些想法整理於[Medium](https://medium.com/unorthodox-paranoid/takeaways-from-js30-challenge-b571c8db862),歡迎與我交流 :) 4 | 5 | ## 01 - JavaScript Drum Kit 6 | 7 | > [Demo](https://hcwxd.github.io/JavaScript30/01%20-%20JavaScript%20Drum%20Kit/index.html) 8 | 9 | - `transitionend` 事件 10 | 11 | - 在 CSS transition 結束後觸發,搭配 remove class 可以做出按鍵被按之後的閃爍效果 12 | 13 | - querySelector 尋找 dataset attribute 符合 14 | 15 | ```JavaScript 16 | querySelector(`div[data-key="${e.keyCode}"]`) 17 | ``` 18 | 19 | - 用 `div[data-key${matching}]` 可以直接在 querySelector 找到特定 node 20 | 21 | - `` 22 | 23 | - 聲音檔案在 Html 上用 audio 包住,src 指定檔案來源 24 | 25 | ```javascript 26 | // 從頭播放 27 | audio.currentTime = 0; 28 | audio.play(); 29 | ``` 30 | 31 | ## 02 - JS and CSS Clock 32 | 33 | > [Demo](https://hcwxd.github.io/JavaScript30/02%20-%20JS%20and%20CSS%20Clock/index.html) 34 | 35 | - `transform-origin` CSS 屬性 36 | 37 | - 參數:x-axis y-axis z-axis 38 | 39 | - `transition-timing-function` CSS 屬性 40 | 41 | - 製造指針擺動效果 42 | 43 | `transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1);` 44 | 45 | ## 03 - CSS Variables 46 | 47 | > [Demo](https://hcwxd.github.io/JavaScript30/03%20-%20CSS%20Variables/index.html) 48 | 49 | - CSS Varialbes 50 | 51 | - 宣告 52 | 53 | ```css 54 | :root { 55 | --spacing: 10px; 56 | --blur: 10px; 57 | --base: #8282e6; 58 | } 59 | ``` 60 | 61 | - 使用 62 | 63 | ```css 64 | img { 65 | padding: var(--spacing); 66 | background: var(--base); 67 | filter: blur(var(--blur)); 68 | } 69 | ``` 70 | 71 | - JS 變更 CSS Variables 72 | 73 | - HTML Input tag 74 | 75 | ```html 76 | 77 | 78 | 79 | 80 | 81 | ``` 82 | 83 | - JS event function 84 | 85 | ```javascript 86 | inputs.forEach((input) => input.addEventListener('change', updateCSS)); 87 | 88 | inputs.forEach((input) => input.addEventListener('mousemove', updateCSS)); 89 | 90 | function updateCSS() { 91 | const suffix = this.dataset.sizing || ''; 92 | document.documentElement.style.setProperty(`--${this.name}`, this.value + suffix); 93 | } 94 | ``` 95 | 96 | - HTML \ 參數 97 | 98 | - type => [Ref link](https://www.w3schools.com/tags/att_input_type.asp) 99 | - [checkbox](https://www.w3schools.com/tags/att_input_type_checkbox.asp) 100 | - [color](https://www.w3schools.com/tags/att_input_type_color.asp) 101 | - [date](https://www.w3schools.com/tags/att_input_type_date.asp) 102 | - [email](https://www.w3schools.com/tags/att_input_type_email.asp) 103 | - [file](https://www.w3schools.com/tags/att_input_type_file.asp) 104 | - [number](https://www.w3schools.com/tags/att_input_type_number.asp) 105 | - [password](https://www.w3schools.com/tags/att_input_type_password.asp) 106 | - [radio](https://www.w3schools.com/tags/att_input_type_radio.asp) 107 | - [range](https://www.w3schools.com/tags/att_input_type_range.asp) 108 | - [reset](https://www.w3schools.com/tags/att_input_type_reset.asp) 109 | - tips 110 | - 用 name 指定變更的 Css Variables 111 | - 用 data-sizing 指定 Css Variables 吃的單位 112 | 113 | ## 04 - Array Cardio Day 1 114 | 115 | > [Demo](https://hcwxd.github.io/JavaScript30/04%20-%20Array%20Cardio%20Day%201/index.html) 116 | 117 | - Filter 118 | 119 | `回傳符合條件的元素組成的陣列` 120 | 121 | ```JavaScript 122 | const fifteen = inventors.filter(inventor => (inventor.year >= 1500 && inventor.year < 1600)) 123 | ``` 124 | 125 | - map 126 | 127 | `回傳透過函式內回傳的值組合成一個陣列` 128 | 129 | ```javascript 130 | const fullName = inventors.map((inventor) => inventor.first + ' ' + inventor.last); 131 | ``` 132 | 133 | - sort 134 | 135 | `回傳符合條件排序後的陣列` 136 | 137 | ```JavaScript 138 | const ordered = inventors.sort((first, second) => first.year > second.year ? 1 : -1); 139 | 140 | const sorted = inventors.sort((first, second) => { 141 | const lastPerson = first.passed - first.year; 142 | const nextPerson = second.passed - second.year; 143 | return lastPerson > nextPerson ? -1 : 1; 144 | }); 145 | 146 | const alpha = people.sort((a, b) => { 147 | const [aLast, aFirst] = a.split(", "); 148 | const [bLast, bFirst] = b.split(", "); 149 | return aLast > bLast ? 1 : -1; 150 | }); 151 | ``` 152 | 153 | - reduce 154 | 155 | `與前一個回傳的值再次作運算,詳細使用為:` 156 | `array.reduce(reducer[accumlator, currentValue, currentIndex, array], initialValue)` 157 | 158 | ```javascript 159 | const totalYears = inventors.reduce((total, inventor) => { 160 | return total + (inventor.passed - inventor.year); 161 | }, 0); 162 | 163 | const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car']; 164 | const transport = data.reduce((obj, item) => { 165 | if (!obj[item]) { 166 | obj[item] = 0; 167 | } 168 | obj[item]++; 169 | return obj; 170 | }, {}); 171 | ``` 172 | 173 | - tips 174 | 175 | - 用 console.table 可以把陣列用 table 方式 log 到瀏覽器的 console 176 | 177 | ## 05 - Flex Panel Gallery 178 | 179 | > [Demo](https://hcwxd.github.io/JavaScript30/05%20-%20Flex%20Panel%20Gallery/index.html) 180 | 181 | - `display: flex` 182 | - 本身為 flex 的元素為 flex-box,而其子元素為 flex-item 183 | - `flex: flex-grow flex-shrink flex-basis` 184 | - flex 決定 flex-item 如何分配 flex-box 的剩餘空間 185 | - flex-grow、flex-shrink 數值皆為相對概念 186 | - 大於 0 即會分配剩餘空間 187 | - flex: 5 為 flex: 1 的五倍大 188 | - grow 決定分配剩餘,shrink 決定如何縮減多餘 189 | - `transition-timing-function` 先縮後放效果 190 | - 效果參數為:cubic-bezier(0.61, -0.19, 0.7, -0.11) 191 | - `classList.toggle(className)` 192 | - 在元素切換一個 CSS,有則 `remove()`,無則 `add()` 193 | - `transitionend` event 194 | - 監聽 transition 結束時觸發,可用 `e.propertyName` 抓到 transition 的事件 195 | - 搭配指定 `e.propertyName` 條件,可以把多個 transition 串起來 196 | - includes 197 | - flex 變化在 chrome 為 flex-grow 事件,在 safari 為 flex 事件,可用 `if (e.propertyName.includes('flex'))` 解決 198 | 199 | ## 06 - Type Ahead 200 | 201 | > [Demo](https://hcwxd.github.io/JavaScript30/06%20-%20Type%20Ahead/index.html) 202 | 203 | - `fetch()` 204 | 205 | - Fetch 為替代[`XMLHttpRequest`](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) 的方案 206 | - fetch(url) 本身會回傳一個 Promise 物件,與 `jQuery.ajax()` 不同點在於,當接收到一個代表錯誤的 HTTP 狀態碼時,從`fetch()`返回的 Promise **不會被標記為** `reject`, 即使該 HTTP 的狀態碼是 404 或 500。相反,它會將 Promise 狀態標記為 resolve (但是會將 resolve 的返回值的 ok 屬性設置為 false ),僅當網絡故障時或請求被阻止時,才會標記為 reject 207 | - `fetch()` 的處理可以用 `.then()` 串接,會得到 `response` 208 | 209 | ```javascript 210 | fetch(url) 211 | .then((blob) => blob.json()) 212 | .then((data) => cities.push(...data)); 213 | ``` 214 | 215 | - blob 命名為 Binary Large Object 的縮寫,通常表一個相當於檔案( Raw data )的不可變物件 216 | - `.json()` 是 response 的 method 217 | - 把回傳陣列裡的物件裡各自塞入大陣列可以直接用 `.push(...data)` 218 | 219 | - 即時監聽 \ 有無變化需要同時監聽兩個事件 220 | 221 | - `change` 222 | - `keyup` 223 | 224 | - 把 array 裡的物件轉成 HTML 的方法 225 | 226 | - `for loop` 227 | 228 | - `map + return + .join('')` 229 | 230 | ```javascript 231 | function displayMatches() { 232 | const matchArray = findMatches(this.value, cities); 233 | const html = matchArray 234 | .map((place) => { 235 | return ` 236 |
  • 237 | ${cityName}, ${stateName} 238 | ${numberWithCommas(place.population)} 239 |
  • 240 | `; 241 | }) 242 | .join(''); 243 | suggestions.innerHTML = html; 244 | } 245 | ``` 246 | 247 | - `.join('')` 是為了把大陣列轉成一個字串 248 | 249 | - `RegExp(wordToMatch, 'gi')` 250 | 251 | - g modifier: global. All matches (don't return on first match) 252 | - i modifier: insensitive. Case insensitive match (ignores case of [a-zA-Z]) 253 | - `.match(regex)` 返回符合的值 254 | - `.replace(regex, replacingWord)` 返回替代後的值 255 | 256 | - 為數字加分隔號 257 | 258 | ```javascript 259 | function numberWithCommas(x) { 260 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); 261 | } 262 | ``` 263 | 264 | - 例子(把 Array 變成 Html,把其中相符的值變色) 265 | 266 | ```javascript 267 | function displayMatches() { 268 | const matchArray = findMatches(this.value, cities); 269 | const html = matchArray 270 | .map((place) => { 271 | const regex = new RegExp(this.value, 'gi'); 272 | const cityName = place.city.replace(regex, `${this.value}`); 273 | const stateName = place.state.replace(regex, `${this.value}`); 274 | return ` 275 |
  • 276 | ${cityName}, ${stateName} 277 | ${numberWithCommas(place.population)} 278 |
  • 279 | `; 280 | }) 281 | .join(''); 282 | suggestions.innerHTML = html; 283 | } 284 | ``` 285 | 286 | ## 07 - Array Cardio Day 2 287 | 288 | > [Demo](https://hcwxd.github.io/JavaScript30/07%20-%20Array%20Cardio%20Day%202/index.html) 289 | 290 | - some 291 | 292 | `檢查陣列中元素,有一元素符合條件則回傳 true` 293 | 294 | ```javascript 295 | const isAdult = people.some((person) => new Date().getFullYear() - person.year >= 19); 296 | ``` 297 | 298 | - every 299 | 300 | `檢查陣列中元素,全部元素符合條件則回傳 true` 301 | 302 | ```javascript 303 | const allAdults = people.every((person) => new Date().getFullYear() - person.year >= 19); 304 | ``` 305 | 306 | - find 307 | 308 | `回傳陣列中第一個符合條件的元素` 309 | 310 | ```javascript 311 | const comment = comments.find((comment) => comment.id === 823423); 312 | ``` 313 | 314 | - findIndex 315 | 316 | `回傳陣列中第一個符合條件的元素索引` 317 | 318 | ```javascript 319 | const index = comments.findIndex((comment) => comment.id === 823423); 320 | 321 | // comments.splice(index, 1); 322 | 323 | const newComments = [...comments.slice(0, index), ...comments.slice(index + 1)]; 324 | ``` 325 | 326 | - splice vs slice 327 | 328 | - `array.splice(start[, deleteCount[, item1[, item2[, ...]]]])` 329 | - `array.slice([begin[, end]])` 330 | - 用 `slice` 組成新陣列,則可用 331 | 332 | ```javascript 333 | const newComments = [...comments.slice(0, index), ...comments.slice(index + 1)]; 334 | ``` 335 | 336 | ## 08 - Fun with HTML5 Canvas 337 | 338 | > [Demo](https://hcwxd.github.io/JavaScript30/08%20-%20Fun%20with%20HTML5%20Canvas/index.html) 339 | 340 | - JS 取得現在視窗大小 341 | 342 | - `window.innerWidth` , `window.innerHeight` 343 | 344 | - Canvas 設置 345 | 346 | - 大小設置 347 | 348 | ```javascript 349 | const canvas = document.querySelector('#draw'); 350 | const ctx = canvas.getContext('2d'); 351 | 352 | canvas.width = window.innerWidth; 353 | canvas.height = window.innerHeight; 354 | ``` 355 | 356 | - [lineCap](https://www.w3schools.com/tags/canvas_linecap.asp) 357 | - 線段結束樣式 358 | - [lineJoin](https://www.w3schools.com/tags/canvas_linejoin.asp) 359 | - 線段相交樣式 360 | - 繪圖流程 361 | 362 | ```javascript 363 | // Start drawing 364 | ctx.beginPath(); 365 | // start from 366 | ctx.moveTo(lastX, lastY); 367 | // go to 368 | ctx.lineTo(e.offsetX, e.offsetY); 369 | // Draw 370 | ctx.stroke(); 371 | ``` 372 | 373 | - Array deconstruct 技巧 374 | 375 | - `[X, Y] = [newX, newY];` 376 | 377 | - hsl 顏色 378 | 379 | - `hsl(hue, saturation, lightness)` 380 | - hue = 0 ~ 360 381 | - saturation, lightness = 0 ~ 100% 382 | 383 | ## 09 - Dev Tools Domination 384 | 385 | > [Demo](https://hcwxd.github.io/JavaScript30/09%20-%20Dev%20Tools%20Domination/index.html) 386 | 387 | - Chrome dev tools 388 | 389 | - 在元素上按右鍵 => break on => attribute modification 390 | 391 | - `console.log()` 392 | 393 | - `%s` => 加入字串 394 | 395 | ```javascript 396 | console.log('Hello I am a %s string!', '💩'); 397 | ``` 398 | 399 | - `%c` => 加入 CSS 400 | 401 | ```javascript 402 | console.log('%c I am some great text', 'font-size:50px; background:red; text-shadow: 10px 10px 0 blue'); 403 | ``` 404 | 405 | - console 系列 406 | 407 | - `console.warn()` 408 | - `console.error()` 409 | - `console.info()` 410 | - `console.assert(statement, 'Word that show when statement == false')` 411 | 412 | ```javascript 413 | console.assert(p.classList.contains('ouch'), 'That is wrong!'); 414 | ``` 415 | 416 | - `console.clear()` 417 | - `console.dir()` 418 | 419 | ```javascript 420 | console.log(p); 421 | console.dir(p); 422 | ``` 423 | 424 | - `console.group()` / `console.groupCollapsed()` + `console.log()` \*n + `console.groupEnd()` 425 | 426 | ```javascript 427 | dogs.forEach((dog) => { 428 | console.groupCollapsed(`${dog.name}`); 429 | console.log(`This is ${dog.name}`); 430 | console.log(`${dog.name} is ${dog.age} years old`); 431 | console.log(`${dog.name} is ${dog.age * 7} dog years old`); 432 | console.groupEnd(`${dog.name}`); 433 | }); 434 | ``` 435 | 436 | - `console.count()` 437 | - `console.time()` + `console.timeEnd()` 438 | 439 | ```javascript 440 | console.time('fetching data'); 441 | fetch('https://api.github.com/users/wesbos') 442 | .then((data) => data.json()) 443 | .then((data) => { 444 | console.timeEnd('fetching data'); 445 | console.log(data); 446 | }); 447 | ``` 448 | 449 | - `console.table()` 450 | 451 | ## 10 - Hold Shift and Check Checkboxes 452 | 453 | > [Demo](https://hcwxd.github.io/JavaScript30/10%20-%20Hold%20Shift%20and%20Check%20Checkboxes/index.html) 454 | 455 | - 偵測使用者用 shift 鍵做選取 456 | 457 | - `e.shiftKey` 458 | 459 | - \[type="checkbox"] 460 | 461 | 用 `input:checked+指定元素` 去操作打勾後的 CSS 變化 462 | 463 | ```javascript 464 | input:checked+p { 465 | background: #F9F9F9; 466 | text-decoration: line-through; 467 | } 468 | ``` 469 | 470 | - 用 !isBoolean 操作 toggle 471 | 472 | ```javascript 473 | if (node === lastChecked || node === this) { 474 | isInBetween = !isInBetween; 475 | } 476 | ``` 477 | 478 | ## 11 - Custom Video Player 479 | 480 | > [Demo](https://hcwxd.github.io/JavaScript30/11%20-%20Custom%20Video%20Player/index.html) 481 | 482 | - \