├── .gitignore ├── 01 JavaScript Drum Kit ├── README.MD ├── index.css ├── index.html └── original │ ├── index-FINISHED.html │ ├── index-START.html │ ├── 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 ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 03 CSS Variables ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 04 Array Cardio Day 1 ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 05 Flex Panel Gallery ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 06 Type Ahead ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css ├── 07 Array Cardio Day 2 ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 08 Fun with HTML5 Canvas ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 09 Dev Tools Domination ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 10 Hold Shift and Check Checkboxes ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 11 Custom Video Player ├── README.MD ├── index.html ├── original │ ├── index.html │ ├── scripts-FINISHED.js │ ├── scripts.js │ └── style.css ├── scripts.js └── style.css ├── 12 Key Sequence Detection ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 13 Slide in on Scroll ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 14 JavaScript References VS Copying ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 15 LocalStorage ├── README.MD ├── index.html ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css └── style.css ├── 16 Mouse Move Shadow ├── README.MD ├── index.html └── original │ ├── index-finished.html │ └── index-start.html ├── 17 Sort Without Articles ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 18 Adding Up Times with Reduce ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 19 Webcam Fun ├── README.MD ├── index.html ├── original │ ├── index.html │ ├── package.json │ ├── scripts-FINISHED.js │ ├── scripts.js │ └── style.css ├── package.json ├── scripts.js └── style.css ├── 20 Speech Detection ├── README.MD ├── index.html ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── package.json └── package.json ├── 21 Geolocation ├── README.MD ├── index.html ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── package.json └── package.json ├── 22 Follow Along Link Highlighter ├── README.MD ├── index.html ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css └── style.css ├── 23 Speech Synthesis ├── README.MD ├── index.html ├── npm-debug.log ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css └── style.css ├── 24 Sticky Nav ├── README.MD ├── index.html ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ ├── style-FINISHED.css │ └── style-START.css └── style.css ├── 25 Event Capture, Propagation, Bubbling and Once ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 26 Stripe Follow Along Nav ├── README.MD ├── index.html └── original │ ├── index-FINISHED.html │ └── index-START.html ├── 27 Click and Drag ├── README.MD ├── index.html ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css └── style.css ├── 28 Video Speed Controller ├── README.MD ├── index.html ├── original │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css └── style.css ├── 29 Countdown Timer ├── README.MD ├── index.html ├── original │ ├── index.html │ ├── scripts-FINISHED.js │ ├── scripts-START.js │ └── style.css ├── scripts.js └── style.css ├── 30 Whack A Mole ├── README.MD ├── dirt.svg ├── index.html ├── mole.svg ├── original │ ├── dirt.svg │ ├── index-FINISHED.html │ ├── index-START.html │ ├── mole.svg │ └── style.css └── style.css ├── README.MD ├── Readme.MD └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 一個鍵盤爵士鼓 4 | - 功能 5 | - 按下鍵盤,出現相對應的聲音 6 | - 畫面 7 | - 按下鍵盤,畫面上的字母方塊,做點小動畫 8 | - 加上黃色陰影 9 | - 按鈕彈一下(微小放大)的動畫 10 | 11 | # Javascript 12 | 13 | ## Arrow Function 14 | - [lambda](http://www.jollen.org/blog/2013/10/javascript-lambda.html) 在Javascript裡叫 `arrow function` 15 | 16 | 一般js的匿名函數這樣寫 17 | ```javascript= 18 | funtion (key) { 19 | key.addEventListener('transitionend', removeTransition); 20 | } 21 | ``` 22 | 用`arrow function`的寫法就是 23 | ```javascript= 24 | key => key.addEventListener('transitionend', removeTransition) 25 | ``` 26 | > 不同之處在於`this`,一般的寫法有`this`=`function`本身,但是「誰」呼叫`arrow function`,「誰」=`this`。 27 | 28 | ## Window方法 29 | 30 | jsref: [The Window Object](https://www.w3schools.com/jsref/obj_window.asp) 31 | - [`setTimeout(function, ms)`](https://www.w3schools.com/jsref/met_win_settimeout.asp) 設定「執行一次」的function與延遲時間 32 | 33 | ## DOM Document 方法 34 | 35 | jsref: [The HTML DOM Document Object](https://www.w3schools.com/jsref/dom_obj_document.asp) 36 | 37 | - `document.querySelector()`找到第一個符合指定CSS selector(s)的HTML節點 38 | - `document.querySelectorAll()`找到符合指定CSS selector(s)的HTML節點們 39 | 40 | ## DOM Element 方法 41 | 42 | jsref: [The HTML DOM Element Object](https://www.w3schools.com/jsref/dom_obj_all.asp) 43 | 44 | - `element.addEventListener()` 加一個事件到Element上 45 | - `element.classList` 回傳NodeList,element上的class屬性的內容 46 | 47 | ## NodeList 方法 48 | jsref: [JavaScript HTML DOM Node List](https://www.w3schools.com/js/js_htmldom_nodelist.asp) 49 | 50 | - `NodeList.forEach()` 51 | 52 | `querySelectorAll()`出來的物件都是`NodeList`,它不是`Array`,能使用的方法不同,在此使用`forEach()`走訪每一個元素。 53 | 54 | 參考: [`Array.forEach`](https://www.w3schools.com/jsref/jsref_forEach.asp) 55 | 56 | ## Event Object 方法 57 | 58 | jsref: [HTML DOM Events](https://www.w3schools.com/jsref/dom_obj_event.asp) 59 | 60 | 61 | - [`Transition Events`](https://www.w3schools.com/jsref/event_transitionend.asp) 過場結束(transition end)執行 myFunction 62 | 63 | 「過場」唯一事件 64 | 65 | ```javascript= 66 | element.addEventListener("transitionend", myFunction) 67 | ``` 68 | -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/index.css: -------------------------------------------------------------------------------- 1 | .key { 2 | width: 32px; 3 | height: 32px; 4 | 5 | border: solid 1px black; 6 | 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | 11 | margin-top: 5px; 12 | 13 | transition: all 0.07s; 14 | } 15 | 16 | .playing { 17 | width: 100px; 18 | } 19 | -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JavaScript Drum Kit 6 | 7 | 8 | 9 |
10 | q 11 |
12 |
13 | w 14 |
15 |
16 | e 17 |
18 |
19 | r 20 |
21 |
22 | t 23 |
24 |
25 | y 26 |
27 |
28 | u 29 |
30 |
31 | i 32 |
33 |
34 | o 35 |
36 |
37 | p 38 |
39 |
40 | a 41 |
42 |
43 | s 44 |
45 |
46 | d 47 |
48 |
49 | f 50 |
51 |
52 | g 53 |
54 |
55 | h 56 |
57 |
58 | j 59 |
60 |
61 | k 62 |
63 |
64 | l 65 |
66 |
67 | ; 68 |
69 |
70 | z 71 |
72 |
73 | x 74 |
75 |
76 | c 77 |
78 |
79 | v 80 |
81 |
82 | b 83 |
84 |
85 | n 86 |
87 |
88 | m 89 |
90 |
91 | , 92 |
93 |
94 | . 95 |
96 |
97 | / 98 |
99 | 100 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS Drum Kit 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | A 14 | clap 15 |
16 |
17 | S 18 | hihat 19 |
20 |
21 | D 22 | kick 23 |
24 |
25 | F 26 | openhat 27 |
28 |
29 | G 30 | boom 31 |
32 |
33 | H 34 | ride 35 |
36 |
37 | J 38 | snare 39 |
40 |
41 | K 42 | tom 43 |
44 |
45 | L 46 | tink 47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS Drum Kit 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | A 14 | clap 15 |
16 |
17 | S 18 | hihat 19 |
20 |
21 | D 22 | kick 23 |
24 |
25 | F 26 | openhat 27 |
28 |
29 | G 30 | boom 31 |
32 |
33 | H 34 | ride 35 |
36 |
37 | J 38 | snare 39 |
40 |
41 | K 42 | tom 43 |
44 |
45 | L 46 | tink 47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/boom.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/boom.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/clap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/clap.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/hihat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/hihat.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/kick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/kick.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/openhat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/openhat.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/ride.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/ride.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/snare.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/snare.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/tink.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/tink.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/sounds/tom.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/01 JavaScript Drum Kit/original/sounds/tom.wav -------------------------------------------------------------------------------- /01 JavaScript Drum Kit/original/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/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 做一個漂亮甜美的時鐘,一開始就提供了一個鐘面,也寫好了三支指針 4 | 5 | - 功能 6 | - 顯示目前的時間 7 | - 更新時針、分針、秒針 8 | - 畫面 9 | - 讀得到目前的時間 10 | - 秒針要有「真實的滴答」動作 11 | - 追加規格 12 | - 指針要有長短(或粗細)可以分辨彼此 13 | - 解決最後的歸零問題 14 | 15 | # CSS 16 | 17 | cssref: [CSS3 transform Property](https://www.w3schools.com/cssref/css3_pr_transform.asp) 18 | 19 | ### 變化(`transform`) 20 | 21 | 可以進行2D和3D的變化,屬性後面可以接幾種能使用的函數 22 | 23 | 1D變化(其實有些是2D適用, 有些是3D適用) 24 | - `translateX(x)` 平移 25 | - `translateY(y)` 平移 26 | - `scaleX(x)` 放大 27 | - `scaleY(y)` 放大 28 | - `scaleZ(z)` 放大 29 | - `rotateX(angle)`, 轉 30 | - `rotateY(angle)`, 轉 31 | - `rotateZ(angle)`, 轉 32 | - `skewX(angle)`, 歪斜 33 | - `skewY(angle)`, 歪斜 34 | 35 | 2D變化 36 | - `matrix(n1,n2,n3,n4,x,y)` 37 | - **n1~n4**:歪斜(看起來像翻轉), 矩形變平行四邊形 38 | - **x,y**: 平移 39 | - `translate(x,y)`, 平移 40 | - `scale(x,y)`, 放大 41 | - `rotate(angle)`, 轉 42 | - `skew(x-angle,y-angle)`, 歪斜 43 | 44 | 3D變化 45 | - `matrix3d 46 | (n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n)`, 歪斜+平移 47 | - `ranslate3d(x,y,z)`, 平移 48 | - `scale3d(x,y,z)`, 放大 49 | - `rotate3d(x,y,z,angle)`, 轉 50 | - `perspective(n)` 透視 51 | 52 | 在此用得到的 53 | ```css= 54 | /*變化: 旋轉(7°);*/ 55 | transform: rotate(7deg); 56 | ``` 57 | 58 | ### 「過場時」的變化函數(`transition-timing-function`) 59 | 60 | cssref: [CSS3 transition-timing-function Property](https://www.w3schools.com/cssref/css3_pr_transition-timing-function.asp) 61 | 62 | 變化的函數,可以設成有回彈效果 63 | 64 | 可用函數(看參考資料了解比較快) 65 | - `ease` 66 | - `linear` 67 | - `ease-in` 類似`cubic-bezier(0.42,0,1,1))` 68 | - `ease-out` 類似`cubic-bezier(0,0,0.58,1))` 69 | - `ease-in-out` 類似`cubic-bezier(0.42,0,0.58,1))` 70 | - `step-star` 71 | - `step-en` 72 | - `steps(int,start|end)` 73 | - `cubic-bezier(n,n,n,n)`, [貝玆函數](http://cubic-bezier.com/#.17,.67,.83,.67) 算是自訂變化曲線函數 74 | 75 | ```css= 76 | transition-timing-function: 函數 77 | ``` 78 | 79 | ### 參考資料 80 | - [linear, ease-in, ease-out, ease-in-out](https://developers.google.com/web/fundamentals/design-and-ui/animations/the-basics-of-easing?hl=zh-tw) 81 | - [transition-timing-function的各種Demo - MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function) 82 | 83 | # Javascript 84 | 85 | ## Window object 方法 86 | 87 | - [`setInterval(function, ms)`](https://www.w3schools.com/jsref/met_win_setinterval.asp) 設定「持續執行」的function與間隔時間 88 | 89 | ## 秒針角度歸零時,不會抖一下 90 | 91 | 秒針抖一下是因為角度歸零。 92 | 93 | 讓角度**不歸零**就可以避免。 94 | -------------------------------------------------------------------------------- /02 JS and CSS Clock/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS + CSS Clock 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 | 19 | 71 | 72 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /02 JS and CSS Clock/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS + CSS Clock 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 | 19 | 68 | 69 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /03 CSS Variables/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 做一個"brand spanking new"的CSS, 這裡的「CSS變數」不是Sass的變數功能。畫面有三個變數: `Space`, `Blur`, `Base Color`,當我們在畫面設定值(拉動range或改變顏色),它會立即的更新畫面 4 | 5 | - 功能 6 | - 用CSS變數 7 | - 追加 8 | - 練習「設定區域變數」 9 | - 畫面 10 | - 改變控制項、立即改變畫面 11 | 12 | 13 | CSS變數可以被Javascript更新, 也就是當你可以在頁面上任何地方更新CSS變數, 變數就可以自動更新。 14 | 15 | > 我知道你會說「wes, we've had variables in SASS forever and this not new」 16 | 17 | 在SASS, 你可以定義、編譯,在變譯之後將無法再改變變數(們) 18 | 19 | ### 參考資料 20 | - [brand spanking new](http://www.phrases.org.uk/meanings/brand-spanking-new.html) 意思是 "Entirely new"(全新的) 21 | - [smart Alec](http://ce.linedict.com/dict.html#/cnen/entry/764905?source=en) 意思是 「自以為是,自作聰明的人」 22 | 23 | # HTML 24 | 25 | ## 嵌入自訂資料進HTML 26 | 27 | - [HTML data-\* Attributes](https://www.w3schools.com/tags/att_global_data.asp) 28 | 29 | 在HTML裡嵌入自定義的`color`資料 30 | ```htmlembedded= 31 | 32 | ``` 33 | 在Javascript取出來的方法,在`element`取`dataset`就可以用相同的「自定義名稱」讀/寫 自定義資料了,資料型態是字串。 34 | ```javascript= 35 | tagNameElement.dataset.color = ""; 36 | ``` 37 | 38 | # CSS 39 | 40 | ## CSS Style Declaration Object 方法 41 | 42 | jsref: [The CSSStyleDeclaration Object](https://www.w3schools.com/jsref/obj_cssstyledeclaration.asp) 43 | - `setProperty(property, value, priority)` 設定CSS的屬性給element(直接加在`style=""`) 44 | 45 | 46 | ### 宣告變數 47 | 使用兩個`-`符號,代表「變數」 48 | 49 | 自定義變數名稱`--main-bg-color`, 值`brown` 50 | ``` 51 | element { 52 | --main-bg-color: brown; 53 | } 54 | ``` 55 | ### JS修改CSS的宣告 56 | 57 | ```Javascript= 58 | element.style.setProperty('--main-bg-color', 'brown'); 59 | ``` 60 | 61 | ### CSS使用變數 62 | 使用`var()`代表「使用變數」,並且將「宣告名稱」丟進來 63 | 64 | 將變數`--main-bg-color`給定進`background-color` 65 | ``` 66 | element { 67 | background-color: var(--main-bg-color); 68 | } 69 | ``` 70 | 71 | 72 | 73 | ## 取HTML element 74 | 75 | 用CSS的`:root`選擇器,可以選到JS的`document.documentElement` 76 | 77 | 在HTML中,root element就是`` 78 | 79 | - [CSS3 :root Selector](https://www.w3schools.com/cssref/sel_root.asp) 80 | 81 | CSS這樣宣告 82 | ```= 83 | :root { 84 | --base: #ff6c00; 85 | --spacing: 10px; 86 | --blur: 10px; 87 | } 88 | ``` 89 | JS就可以這樣改寫 90 | ```Javascript= 91 | document.documentElement.style.setProperty(`--${this.name}`, this.value); 92 | ``` 93 | ### 參考資料 94 | - [CSS變數的瀏覽器支援](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) 95 | 96 | 97 | # Javascript 98 | 99 | ## DOM方法 100 | 101 | jsref: The HTML DOM Document Object 102 | 103 | - [`document.documentElement`](https://www.w3schools.com/jsref/prop_document_documentelement.asp) 回傳``的節點 104 | -------------------------------------------------------------------------------- /03 CSS Variables/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Update CSS Variables with 9 | JS 10 |

11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 62 | 63 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /03 CSS Variables/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Scoped CSS Variables and JS 6 | 7 | 8 |

Update CSS Variables with JS

9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 61 | 62 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /03 CSS Variables/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Scoped CSS Variables and JS 6 | 7 | 8 |

Update CSS Variables with JS

9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 46 | 47 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /04 Array Cardio Day 1/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 希望你今天有出汗的心理準備, 我們不會做什麼漂亮的東西 4 | 5 | 沒有CSS沒有DOM element, 就是Javascript基本的Array方法運用 6 | 7 | 包含`filter`, `map`, `sort`, `reduce`這些方法 8 | 9 | 在此提供了`inventors`, `people`陣列, 有一些題目, 每一題會指定一個Array Method解題 10 | 11 | > 第六題: 需要進入[Category:Boulevards in Paris]( 12 | https://en.wikipedia.org/wiki/Category:Boulevards_in_Paris) 頁面,打開`console`進行解題 13 | 14 | # JavaScript 15 | 16 | ## Array 方法 17 | 18 | jsref: [JavaScript Array Reference](https://www.w3schools.com/jsref/jsref_obj_array.asp) 19 | - [`Array.filter()`](https://www.w3schools.com/jsref/jsref_filter.asp) 過濾, 函數回傳 20 | - `true`: 表示「符合條件」 21 | - `false`: 表示「不符合條件」 22 | - [`Array.reduce()`](https://www.w3schools.com/jsref/jsref_reduce.asp) 累積, 函數通常是「累加」 23 | - `reduce(function(total, everyone, currIndex, currArray), initValue)` 24 | - total, 跨元素的暫存變數 25 | - everyone, 每一個元素 26 | - currIndex, 目前的索引值 27 | - 有給initValue就0起始, 初始值自訂 28 | - 沒有initValue就1起始, 初始值為第一個元素 29 | - currArray, 整個陣列 30 | - initValue, 初始值\ 31 | - `Array.from(NodeList)` 將NoteList轉成Array 32 | - 也可以用`[...NodeList]` 33 | - [`Array.sort()`](https://www.w3schools.com/jsref/jsref_sort.asp) 排序, 可用數字相減值回傳, 函數回傳值 34 | - +1: 大於 35 | - 0: 等於 36 | - -1: 小於 37 | 38 | ### 特別說一下`sort` 39 | 40 | [`Array.sort()`](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 41 | 42 | ```javascript= 43 | b.sort(function(a, b){ return b - a;});//[576, 56, 12, 9, 8, 8, 5, 3, 1, 0] 44 | b.sort(function(a, b){ return a - b;});//[0, 1, 3, 5, 8, 8, 9, 12, 56, 576] 45 | ``` 46 | > 建議使用`-`而不是用`>`或`<`。 47 | > 原因: 48 | > - 用`-`回傳`>0`, `=0`, `<0` 49 | > - 用`>`或`<`回傳`true`, `false` 50 | > [參考資料](http://stackoverflow.com/questions/1063007/how-to-sort-an-array-of-integers-correctly) 51 | 52 | 53 | ## String 方法 54 | - `String.includes('keywork')` 找字串是否有包含`keywork` 55 | -------------------------------------------------------------------------------- /04 Array Cardio Day 1/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Array Cardio 💪 6 | 7 | 8 |

Psst: have a look at the JavaScript Console 💁

9 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /05 Flex Panel Gallery/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要做一個超級迷人,我喜歡稱之為flex gallery。 4 | 5 | 沒什麼Javascript、很多的CSS用flexbox、用transitions 6 | 7 | - 功能 8 | - 點擊指定的區塊要以它為主, 畫面變化 9 | - 再點擊一次,恢復原狀 10 | - 畫面 11 | - 主要區塊變大, 中間的字變大 12 | - 變大之後上下的字要進來 13 | 14 | # CSS 15 | 16 | ## flexbox 17 | 18 | ### 學習資源 19 | 20 | - [Flex.io](https://www.flex.io/) 21 | - [FLEX - CSS-TRICKS](https://css-tricks.com/almanac/properties/f/flex/) 22 | 23 | ## flex 24 | 25 | 容器要先設定 `display: flex;` 26 | 27 | 元素要設定`flex: 1 0 auto;` 28 | 29 | ### flex語法解釋 30 | 31 | 要解譯這一個之前,必須了解一個詞叫「剩餘空間」([超推薦看!!出處](http://zhoon.github.io/css3/2014/08/23/flex.html)) 32 | 33 | > `justify-content` 的屬性會失效 34 | 35 | `flex`是下面三者的速寫 36 | The second and third parameters (flex-shrink and flex-basis) are optional. 37 | 1. [`flex-grow`](https://css-tricks.com/almanac/properties/f/flex-grow/) 剩餘空間分配比例 38 | 2. [`flex-shirk`](https://css-tricks.com/almanac/properties/f/flex-shrink/) 壓縮比例 39 | 3. [`flex-basis`](https://css-tricks.com/almanac/properties/f/flex-basis/) 預約剩餘空間 40 | -------------------------------------------------------------------------------- /05 Flex Panel Gallery/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flex Panels 💪 6 | 7 | 8 | 9 | 74 | 75 | 76 |
77 |
78 |

Hey

79 |

Let's

80 |

Dance

81 |
82 |
83 |

Give

84 |

Take

85 |

Receive

86 |
87 |
88 |

Experience

89 |

It

90 |

Today

91 |
92 |
93 |

Give

94 |

All

95 |

You can

96 |
97 |
98 |

Life

99 |

In

100 |

Motion

101 |
102 |
103 | 104 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /06 Type Ahead/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 要做一個我喜歡稱為`type-ahead feature`(提示功能), 在此我們有一個擁有歡迎程度的cities和states列表, 當我們keyin一些關鍵字,此功能可以幫我找到在cities和states裡符合關鍵字的項目。 4 | 5 | - 功能 6 | - 列出符合關鍵字的項目 7 | - 畫面 8 | - 列出符合關鍵字的項目 9 | - 將關鍵字的部份mark起來 10 | - 顯示該項目的「歡迎程度」數字 11 | 12 | 13 | # Javascript 14 | 15 | > Keyword: [blob](https://zh.wikipedia.org/wiki/%E4%BA%8C%E9%80%B2%E4%BD%8D%E5%A4%A7%E5%9E%8B%E7%89%A9%E4%BB%B6) 二進位大型物件 16 | 17 | > 二進位大型物件(英語:binary large object ,或英語:basic large object,縮寫為Blob、BLOB、BLOb),在資料庫管理系統中,將二進位資料儲存為一個單一個體的集合。Blob通常是影像、聲音或多媒體檔案。 18 | > 19 | > 它由迪吉多公司的工程師吉姆·史塔基(Jim Starkey)發明。 20 | 21 | ## API 22 | - [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) 一種非同步的..API, 向後端要資料, 回傳`promise` 23 | - [response object](https://developer.mozilla.org/en-US/docs/Web/API/Response) 24 | - [promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise) 非同步回傳的結果 25 | - [promise.then()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) 處理 `promise`的方法 26 | 27 | ## JavaScript RegExp Reference 28 | 29 | jsref: [RegExp Object](https://www.w3schools.com/jsref/jsref_obj_regexp.asp) 30 | 31 | 正規表示式, 常用於文字(字串)的過濾、搜尋、取代方法 32 | 33 | ### 使用`replace`將字mark起來 34 | 35 | 下面的例子, 將`key=this.value` 36 | ```javascript= 37 | const regex = new RegExp(this.value, 'gi'); 38 | str.replace(regex, `${this.value}`); 39 | ``` 40 | -------------------------------------------------------------------------------- /06 Type Ahead/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Type Ahead 👀 6 | 7 | 8 | 9 | 10 |
11 | 12 | 16 |
17 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /06 Type Ahead/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Type Ahead 👀 6 | 7 | 8 | 9 | 10 |
11 | 12 | 16 |
17 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /06 Type Ahead/original/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 | } 72 | 73 | a { 74 | color:black; 75 | background:rgba(0,0,0,0.1); 76 | text-decoration: none; 77 | } 78 | -------------------------------------------------------------------------------- /07 Array Cardio Day 2/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天是Array Cardio 2(陣列有氧運動2), 我們將學到新的一些Array methods。 4 | 5 | 有兩個陣列 `people`、`comments`, 完成題目的要求。 6 | 7 | 題目以`Array.prototype.method()`為分界。 8 | 9 | 看一下題目多長是下一題 10 | 11 | 會練習到這些方法 12 | - `Array.prototype.some()` 13 | - `Array.prototype.every()` 14 | - `Array.prototype.find()` 15 | - `Array.prototype.findIndex()` 16 | 17 | # JavaScript 18 | 19 | ## Array 方法 20 | jsref: [JavaScript Array Reference](https://www.w3schools.com/jsref/jsref_obj_array.asp) 21 | - [`Array.some()`](https://www.w3schools.com/jsref/jsref_some.asp) Checks if any of the elements in an array pass a test 22 | - [`Array.every()`](https://www.w3schools.com/jsref/jsref_every.asp) Checks if every element in an array pass a test 23 | - [`Array.find()`](https://www.w3schools.com/jsref/jsref_find.asp) Returns the value of the first element in an array that pass a test 24 | - [`Array.findIndex()`](https://www.w3schools.com/jsref/jsref_findindex.asp) Returns the index of the first element in an array that pass a test 25 | - [`Array.slice()`](https://www.w3schools.com/jsref/jsref_slice_array.asp) Selects a part of an array, and returns the new array 26 | - [`Array.splice()`](https://www.w3schools.com/jsref/jsref_splice.asp) Adds/Removes elements from an array 27 | 28 | ## 超接近的字 29 | - slice 切片 30 | - splice 拼接 31 | 32 | > delete 會造成undefine, index不連續 33 | 用splice就不會,(用新的array接舊有的元素) 34 | -------------------------------------------------------------------------------- /07 Array Cardio Day 2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Array Cardio 💪💪 6 | 7 | 8 |

Psst: have a look at the JavaScript Console 💁

9 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /07 Array Cardio Day 2/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Array Cardio 💪💪 6 | 7 | 8 |

Psst: have a look at the JavaScript Console 💁

9 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /07 Array Cardio Day 2/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Array Cardio 💪💪 6 | 7 | 8 |

Psst: have a look at the JavaScript Console 💁

9 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /08 Fun with HTML5 Canvas/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要做一個甜美的 `HTML5 canvas` 4 | 5 | - 功能 6 | - 可以畫圖的書布 7 | - 粗細變化 8 | - 顏色變化 9 | - 畫面 10 | - 邊畫邊換色(只有新畫出來的換色) 11 | - 邊畫邊換粗細 12 | - 停下來,再繼續畫,粗細和顏色與上次停筆相同 13 | 14 | # HSL色彩空間 15 | 16 | [小工具](http://mothereffinghsl.com/) 17 | 18 | # Javascript 19 | 20 | ## [`HTML5 Canvas`](https://www.w3schools.com/html/html5_canvas.asp) 21 | 22 | ```javascript= 23 | var htmlCanvas = document.getElementById("myCanvas"); 24 | var canvasContext = htmlCanvas.getContext("2d"); 25 | canvasContext.beginPath(); 26 | canvasContext.arc(95,50,40,0,2*Math.PI); 27 | canvasContext.stroke(); 28 | ``` 29 | 30 | 31 | 32 | ## [CanvasRenderingContext2D](https://developer.mozilla.org/zh-TW/docs/Web/API/CanvasRenderingContext2D) 33 | 34 | ```javascript= 35 | var htmlCanvas = document.getElementById("myCanvas"); 36 | var ctx = htmlCanvas.getContext("2d"); 37 | ``` 38 | 39 | - ctx.strokeStyle = '#BADA55'; 線的顏色 40 | - Color or style to use for the lines around shapes. 41 | - Default #000 (black). 42 | - ctx.lineJoin = 'round'; 43 | - Defines the type of corners where two lines meet. 44 | - Possible values: round, bevel, miter (default). 45 | - ctx.lineCap = 'round'; 46 | - Type of endings on the end of lines. 47 | - Possible values: butt (default), round, square. 48 | - ctx.beginPath(); 49 | - Starts a new path by emptying the list of sub-paths. 50 | - Call this method when you want to create a new path. 51 | - [ctx.globalCompositeOperation](https://developer.mozilla.org/zh-TW/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation) 有光學合成效果 52 | -------------------------------------------------------------------------------- /08 Fun with HTML5 Canvas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML5 Canvas 6 | 7 | 8 | 9 | 62 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /08 Fun with HTML5 Canvas/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML5 Canvas 6 | 7 | 8 | 9 | 65 | 66 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /08 Fun with HTML5 Canvas/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML5 Canvas 6 | 7 | 8 | 9 | 11 | 12 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /09 Dev Tools Domination/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 這是一個開發工具和console技巧的快速教學影片。 4 | 5 | 一段教學,一段練習的方式進行 6 | 7 | # Go!!! 8 | 9 | 可以在HTML element下中斷點 10 | 11 | 直接看code就知道了!!! 12 | 13 | ## Regular (常見的正規用法) 14 | 印出常數 15 | ``` 16 | console.log('aaa'); 17 | ``` 18 | 印出變數 19 | ``` 20 | var a = 'aaa'; 21 | console.loglog(a); 22 | ``` 23 | 24 | ## 格式化輸出的用法 Interpolated (以內插值替換) 25 | 26 | 像C語言的 `printf()` 這樣使用 27 | 28 | ``` 29 | console.log('%s', aaa); 30 | ``` 31 | 32 | ### 用ES6的 template string (ES6 back-ticks) 33 | 34 | ``` 35 | var a = 'ES6'; 36 | console.log(`hello ${a}`); 37 | ``` 38 | 39 | ## 用inline CSS (Styled) 40 | 41 | 用Interpolated時,可以使用style定義 42 | 43 | ``` 44 | console.log("%cDon't do this", 'font-size: 48pt; color: red;'); 45 | ``` 46 | 47 | 用HTML表示就等同於在console.log印出這樣的字 48 | 49 | ``` 50 | Don't do this 51 | ``` 52 | 53 | ## 強調訊息類型 54 | Warning! 55 | ``` 56 | console.warn('Oh Nooo'); 57 | ``` 58 | Error 59 | ``` 60 | console.error('Oh Nooo'); 61 | ``` 62 | Info 63 | ``` 64 | console.info('this is info'); 65 | ``` 66 | 67 | ## 測試常用 (Testing) 68 | 69 | ### Assert 70 | 71 | ``` 72 | console.assert(1===1, 'this is wronign'); 73 | console.assert(1===2, 'this is wronign'); 74 | ``` 75 | ## 清掉之前印出的東西 (clearing) 76 | 清掉目前 `console` 裡的東西 77 | 78 | `console.clear();` 79 | 80 | ## 印出內容 81 | Viewing DOM Elements 印出 可展開的node 82 | 83 | ### 印出簡單變數 84 | `console.log(p);` 85 | 86 | #### 印出來要「群組化顯示」 87 | Grouping together log群組化印出 88 | 89 | 預設展開 90 | ``` 91 | console.group(`${dog.name}`); 92 | //放入你要印出來的內容(簡單變數).... 93 | console.groupEnd(`${dog.name}`); 94 | ``` 95 | 預設縮起來 96 | ``` 97 | console.groupCollapsed(`${dog.name}`); 98 | //放入你要印出來的內容(簡單變數).... 99 | console.groupEnd(`${dog.name}`); 100 | ``` 101 | 102 | ### 印出物件 103 | 104 | ``` 105 | var aObject = { 106 | name: 'chris', 107 | age: 33 108 | } 109 | console.dir(aObject); 110 | ``` 111 | 112 | ### 印出陣列 113 | 114 | ``` 115 | var aArray = [{ 116 | name: 'chris', 117 | age: 33 118 | }, 119 | { 120 | name: 'chris', 121 | age: 28 122 | }] 123 | console.table(aArray); 124 | ``` 125 | 126 | ## 計數 (counting) 127 | 128 | 立即印出「內容」和「出現次數」 129 | 130 | ``` 131 | console.count('aaaaaa'); 132 | console.count('bbbbbb'); 133 | console.count('aaaaaa'); 134 | console.count('bbbbbb'); 135 | console.count('aaaaaa'); 136 | console.count('bbbbbb'); 137 | ``` 138 | 139 | ## 計時 (timing) 140 | 141 | ``` 142 | console.time('fetching data'); 143 | fetch('https://api.github.com/users/webos') 144 | .then(data => data.json()) 145 | .then(data => { 146 | console.timeEnd('fetching data'); 147 | console.log(data); 148 | }); 149 | ``` 150 | -------------------------------------------------------------------------------- /09 Dev Tools Domination/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Console Tricks! 8 | 9 | 10 | 11 |

×BREAK×DOWN×

12 | 13 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /09 Dev Tools Domination/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Console Tricks! 6 | 7 | 8 | 9 |

×BREAK×DOWN×

10 | 11 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /09 Dev Tools Domination/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Console Tricks! 6 | 7 | 8 | 9 |

×BREAK×DOWN×

10 | 11 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /10 Hold Shift and Check Checkboxes/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天的題目算是a bit of a tough one。 4 | 5 | 一開始看到一個checkbox們,你對其中一個打勾,再按住`shift`對另一個打勾,中間的選項會自動勾起來。 6 | 7 | 不管是由上選下來,還是由下選上來 8 | 9 | - 功能 10 | - 像Gmail選取郵件這樣,可以先選一個,再「按住`shift`」勾選,進行多選的效果。 11 | - 畫面 12 | - 滑鼠點覽checkbox,要打勾,再點擊則取消 13 | - 「按住`shift`」勾選,若畫面上已有打勾,則此次和上次打勾的選項中間都要自動打勾 14 | - 追加 15 | - 第三次打勾...自己要勾起來,已勾過的到自己中間的也都要勾起來,但是己勾過的不能改變。 16 | 17 | # Javascript 18 | 19 | ## 程式撰寫思維 20 | 21 | > 以前都寫C++。 22 | > 物件長怎樣 就要依序存取進去,由外而內。 23 | 24 | 不同於以往的程式撰寫思維。 25 | 在此,程式操作只要針對`` 26 | 所以,影片中就直接把``抓出來處理。 27 | 28 | > 長得相同,實則不同 29 | 30 | 在`querySelector`完之後,列出來的` 56 | 62 |
63 |
64 | 65 |

This is an inbox layout.

66 |
67 |
68 | 69 |

Check one item

70 |
71 |
72 | 73 |

Hold down your Shift key

74 |
75 |
76 | 77 |

Check a lower item

78 |
79 |
80 | 81 |

Everything inbetween should also be set to checked

82 |
83 |
84 | 85 |

Try do it with out any libraries

86 |
87 |
88 | 89 |

Just regular JavaScript

90 |
91 |
92 | 93 |

Good Luck!

94 |
95 |
96 | 97 |

Don't forget to tweet your result!

98 |
99 |
100 | 101 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /11 Custom Video Player/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要做一個`HTML5 video`播放器,要試著改變video的預設style。 4 | 5 | 每一個不同的browser看起來的預設介面都不太相同。 6 | 7 | 預設介面隱藏控制介面,還會剩下一些調整選項。 8 | 9 | - 功能 10 | - 和預設介面相同的功能 11 | - play、pause、跳到指定時間、音量調整、~~全螢幕~~、播放速度、往前10秒、往後5秒 12 | - 畫面 13 | - 依題目程式碼給的 14 | - 縮起來剩下進度條(已給) 15 | - Hover整個出現(已給) 16 | 17 | 18 | # HTML 19 | 20 | ## HTML Video 21 | 22 | html tag: (HTML Audio/Video DOM Reference)[https://www.w3schools.com/tags/ref_av_dom.asp] 23 | 24 | ### Event 25 | - click 26 | - pause 27 | - play 28 | - timeupdate 29 | 30 | ### 設計原則 31 | 32 | > 使用者操作-> 33 | 觸發介面元件事件->改變真實程式物件-> 34 | 觸發程式物件事件->改變介面 35 | 36 | 將介面與程式切割開來 37 | 38 | ## 更棒的寫法 39 | 40 | ```javascript 41 | let mousedown = false; 42 | progress.addEventListener('click', scrub); 43 | progress.addEventListener('mousemove', (e) => { 44 | if (mousedown) 45 | scrub(e); 46 | }); 47 | progress.addEventListener('mousedown', () => mousedown = true); 48 | progress.addEventListener('mouseup', () => mousedown = false); 49 | 50 | ``` 51 | ```javascript 52 | let mousedown = false; 53 | progress.addEventListener('click', scrub); 54 | progress.addEventListener('mousemove', (e) => mousedown && scrub(e)); 55 | progress.addEventListener('mousedown', () => mousedown = true); 56 | progress.addEventListener('mouseup', () => mousedown = false); 57 | ``` 58 | -------------------------------------------------------------------------------- /11 Custom Video Player/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML Video Player 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 |
15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /11 Custom Video Player/original/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML Video Player 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 |
15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /11 Custom Video Player/original/scripts-FINISHED.js: -------------------------------------------------------------------------------- 1 | /* Get Our 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 out functions */ 11 | function togglePlay() { 12 | const method = video.paused ? 'play' : 'pause'; 13 | video[method](); 14 | } 15 | 16 | function updateButton() { 17 | const icon = this.paused ? '►' : '❚ ❚'; 18 | console.log(icon); 19 | toggle.textContent = icon; 20 | } 21 | 22 | function skip() { 23 | video.currentTime += parseFloat(this.dataset.skip); 24 | } 25 | 26 | function handleRangeUpdate() { 27 | video[this.name] = this.value; 28 | } 29 | 30 | function handleProgress() { 31 | const percent = (video.currentTime / video.duration) * 100; 32 | progressBar.style.flexBasis = `${percent}%`; 33 | } 34 | 35 | function scrub(e) { 36 | const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration; 37 | video.currentTime = scrubTime; 38 | } 39 | 40 | /* Hook up the event listners */ 41 | video.addEventListener('click', togglePlay); 42 | video.addEventListener('play', updateButton); 43 | video.addEventListener('pause', updateButton); 44 | video.addEventListener('timeupdate', handleProgress); 45 | 46 | toggle.addEventListener('click', togglePlay); 47 | skipButtons.forEach(button => button.addEventListener('click', skip)); 48 | ranges.forEach(range => range.addEventListener('change', handleRangeUpdate)); 49 | ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate)); 50 | 51 | let mousedown = false; 52 | progress.addEventListener('click', scrub); 53 | progress.addEventListener('mousemove', (e) => mousedown && scrub(e)); 54 | progress.addEventListener('mousedown', () => mousedown = true); 55 | progress.addEventListener('mouseup', () => mousedown = false); 56 | -------------------------------------------------------------------------------- /11 Custom Video Player/original/scripts.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/11 Custom Video Player/original/scripts.js -------------------------------------------------------------------------------- /11 Custom Video Player/scripts.js: -------------------------------------------------------------------------------- 1 | //click view play and pause 2 | function switchPlayAndPause() { 3 | if(video_application.paused) { 4 | video_application.play(); 5 | } 6 | else { 7 | video_application.pause(); 8 | } 9 | } 10 | const video_application = document.querySelector('.player__video'); 11 | const play_or_pause_ctrl = document.querySelector('.player__button[title="Toggle Play"]'); 12 | 13 | //play button 14 | video_application.addEventListener('click', switchPlayAndPause); 15 | play_or_pause_ctrl.addEventListener('click', switchPlayAndPause); 16 | 17 | //video event update icon 18 | function updateIcon() { 19 | const ctrl = document.querySelector('.player__button[title="Toggle Play"]'); 20 | if(video_application.paused) { 21 | ctrl.textContent = '>'; 22 | } 23 | else { 24 | ctrl.textContent = '||'; 25 | } 26 | } 27 | video_application.addEventListener('play', updateIcon); 28 | video_application.addEventListener('pause', updateIcon); 29 | play_or_pause_ctrl.textContent = '[]'; 30 | 31 | //process 32 | // initial process 33 | const process = document.querySelector('.progress__filled'); 34 | process.style.flexBasis = '0%'; 35 | video_application.currentTime = 0; 36 | 37 | //show 38 | function updateProcessView(e) { 39 | const currWatched = ((video_application.currentTime / video_application.duration) * 100) 40 | .toPrecision(2) + '%'; 41 | process.style.flexBasis = currWatched; 42 | } 43 | 44 | //change 45 | function changeProcess(e) { 46 | const currWatched = (e.layerX / process_all.clientWidth) * video_application.duration; 47 | video_application.currentTime = currWatched; 48 | } 49 | 50 | 51 | const process_all = document.querySelector('.progress'); 52 | process_all.addEventListener('click', changeProcess); 53 | video_application.addEventListener('timeupdate', updateProcessView); 54 | 55 | 56 | //range ctrl 57 | function changeRange() { 58 | video_application[this.name] = this.value; 59 | } 60 | 61 | // initial volume 62 | video_application.volume = 0.5; 63 | // initial playbackRate 64 | video_application.playbackRate = 1; 65 | // playbackRate 66 | const range_ctrls = document.querySelectorAll('.player__slider'); 67 | range_ctrls.forEach(ctrl => { 68 | ctrl.addEventListener('change', changeRange); 69 | ctrl.addEventListener('mousemove', changeRange); 70 | }); 71 | 72 | //player__button add 10s 73 | function skip() { 74 | video_application.currentTime += Number(this.dataset.skip); 75 | } 76 | 77 | const jump_sec_ctrls = document.querySelectorAll('[data-skip]'); 78 | jump_sec_ctrls.forEach(ctrl => 79 | ctrl.addEventListener('click', skip) 80 | ); 81 | -------------------------------------------------------------------------------- /12 Key Sequence Detection/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 做一個打密碼出現彩蛋的功能,像是KONAME code這樣 4 | 5 | > 翻譯原文(大概的意思,應該與上一面描述相同) 6 | > 7 | > 做一個鍵盤輸入序列檢測(), 意思是在視窗輸入一定數量的「鍵」, 就會發生特定的事件。最耳熟能詳的就是KONAME code, 如果你在1980或1990年代時期長大, 你就知道輸入`↑, ↑, ↓, ↓, ←, →, ←, →, B, A, start`就可以觸發類似超級馬莉中無限生命的功能 8 | 若你在[網站](https://www.buzzfeed.com/)輸入`↑, ↑, ↓, ↓, ←, →, ←, →, B, A, start`, 它會將所有的字改為`Wilkie`,我不知道是誰,但是很有趣 不同的網站一些不同的復活節彩蛋,你可以去看看。 9 | 10 | 此例實做的密碼是`wesbos`, 不過練習的話可以自行定義!! 11 | 12 | 13 | # Javascript 14 | 15 | ## [`splice`](https://www.w3schools.com/jsref/jsref_splice.asp) 16 | 17 | ```javascript 18 | pressed.splice(-secretCode.length-1, pressed.length - secretCode.length); 19 | ``` 20 | 21 | ## Syntax 22 | array.splice(index, howmany, item1, ....., itemX) 23 | 24 | - index < 0, 從後面數過來 25 | - howmany, 刪掉的個數 26 | -------------------------------------------------------------------------------- /12 Key Sequence Detection/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /12 Key Sequence Detection/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Key Detection 6 | 7 | 8 | 9 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /12 Key Sequence Detection/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Key Detection 6 | 7 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /13 Slide in on Scroll/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要做一個slide in on scroll, 捲到特定位置,載入特定圖片 4 | 5 | - 功能 6 | - 捲到特定位置, 載入特定圖片 7 | - 畫面 8 | - 進入畫面時, 靠左的圖, 從左邊滑入, 靠右邊的圖, 從右邊滑入 9 | - 滑入同時, 要從透明漸漸出現 10 | - 離開畫面時, 靠左的圖, 從左邊滑出, 靠右邊的圖, 從右邊滑出 11 | - 滑入同時, 要從漸漸消失至透明 12 | 13 | # Javascript 14 | 15 | ## Window Event Object 16 | 17 | ### Drag Events 18 | 19 | - [onscroll](https://www.w3schools.com/tags/ev_onscroll.asp), Script to be run when an element's scrollbar is being scrolled 20 | 21 | ## 閉包 22 | 23 | 滑動延遲反應 24 | 25 | ```javascript= 26 | function debounce(func, wait = 20, immediate = true) { 27 | var timeout; 28 | 29 | // console.log('debounce()'); 30 | //滑一次執行一次A 31 | return function() { 32 | // console.log('A() start'); 33 | var context = this, args = arguments; //this = window object 34 | 35 | //最後一次滑動之後執行的內容 36 | var later = function() { 37 | // console.log('laterRun() start'); 38 | timeout = null;//清掉等待執行數字 39 | if (!immediate) func.apply(context, args); //沒有要立即執行的就上了 40 | // console.log('laterRun() end'); 41 | }; 42 | 43 | var callNow = immediate && !timeout; 44 | //true = 立即 && 無等待執行(滑動停止,時間間隔到)(每次間隔切換一次) 45 | //false = 立即 && 有等待執行(連續滑動中) 46 | //false = 不立即 && 無等待執行(滑動停止,時間間隔到) 47 | //false = 不立即 && 有等待執行(連續滑動中) 48 | 49 | //連續滑動時,延遲等待執行 50 | clearTimeout(timeout);//時間還沒到的時候,就等待取消 51 | timeout = setTimeout(later, wait); //設定等待執行, 紀錄等待執行數字 52 | 53 | 54 | if (callNow) func.apply(context, args);//如果立即執行,就用像原本這樣執行 55 | }; 56 | } 57 | 58 | //... 59 | document.addEventListener('scroll', debounce(checkSlide)); 60 | 61 | ``` 62 | -------------------------------------------------------------------------------- /14 JavaScript References VS Copying/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天來做做Javascript reference 與 copy,可以稍稍的了解一下Javascript是怎麼運作的 4 | 5 | 有些bug來自大家不了解reference在copy的特別之處,尤其是巢狀式物件 6 | 7 | 就依下面的題目一題一題做吧(我也看不懂如何分題目@@) 8 | 9 | # Javascript 10 | 11 | ## Array Clone 12 | ```javascript= 13 | const copy_players1 = players.slice(); 14 | const copy_players2 = [].concat(players); 15 | const copy_players3 = [...players]; 16 | const copy_players4 = Array.from(players); 17 | ``` 18 | 19 | ## Object Clone 20 | ### 深層(一層)Clone 21 | - `Object.assign()` object to object 22 | ```javascript= 23 | const dev = Object.assign({}, wes); 24 | ``` 25 | 26 | ### 深層(多層)Clone 27 | - [`JSON.parse()`](https://www.w3schools.com/js/js_json_parse.asp) string to json 28 | - [`JSON.stringify()`](https://www.w3schools.com/js/js_json_stringify.asp) json to string 29 | 30 | ```javascript= 31 | const dev2 = JSON.parse(JSON.stringfy(wes)); 32 | ``` 33 | -------------------------------------------------------------------------------- /14 JavaScript References VS Copying/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS Reference VS Copy 6 | 7 | 8 | 9 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /14 JavaScript References VS Copying/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS Reference VS Copy 6 | 7 | 8 | 9 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /14 JavaScript References VS Copying/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS Reference VS Copy 6 | 7 | 8 | 9 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /15 LocalStorage/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天主要學兩個東西。 4 | 1. localStorage 5 | 2. event delegation 6 | 7 | - 功能 8 | - 做一個可以輸入文字的框,送出之後,新增到上面打勾的選項 9 | - 重新整理時,選單不會清空 10 | - 畫面 11 | - 選單的選項前面,要有打勾的功能,並且自訂圖示。 12 | - 不打勾: 方塊 13 | - 打勾: 一塊"pork taco" 14 | 15 | # JavaScript 16 | 17 | ## DOM Object 18 | [HTML DOM appendChild() Method](https://www.w3schools.com/jsref/met_node_appendchild.asp) 將node放入node的裡面(下一層)。 19 | 20 | ## Event Object 21 | - [preventDefault() Event Method](https://www.w3schools.com/jsref/event_preventdefault.asp) 取消Event的預設行為 22 | - [Event.target](https://www.w3schools.com/jsref/event_target.asp) 回傳「觸發了誰」 23 | 24 | ## label 加入for 25 | ```javascript= 26 | label.setAttribute('for', input.id); 27 | ``` 28 | 29 | ## 清空form 30 | ```javascript= 31 | form.reset(); 32 | ``` 33 | ## [`localStorage`](https://www.w3schools.com/html/html5_webstorage.asp) 物件 34 | 35 | `localStorage`比`cookie`更適合拿來做暫存的小空間。 36 | 37 | - localStorage.setItem('key', 'value'); 38 | - localStorage.getItem('key'); 39 | - localStorage.removeItem('key'); 40 | 41 | 另外還有`sessionStorage` 42 | 43 | ## 更棒的寫法 44 | 45 | 原本的寫法 46 | ```javascript= 47 | let items = []; 48 | if (JSON.parse(localStorage.getItem('items'))){ 49 | items = JSON.parse(localStorage.getItem('items')); 50 | } 51 | populateList(items, itemsList); //update view 52 | 53 | ``` 54 | 改成 55 | ```javascript= 56 | const items = JSON.parse(localStorage.getItem('items')) || []; 57 | populateList(items, itemsList); //update view 58 | ``` 59 | 60 | ## 問題 61 | 62 | 1. 為什麼不在label上面掛click的事件? 63 | 2. 最後加上去更新畫面的(`Line 70: populateList(items, itemsList);`)是否必要? 64 | -------------------------------------------------------------------------------- /15 LocalStorage/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LocalStorage 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 |
17 |

LOCAL TAPAS

18 |

19 | 22 |
23 | 24 | 25 |
26 |
27 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /15 LocalStorage/original/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 | -------------------------------------------------------------------------------- /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/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要學習滑鼠移動事件,移動滑鼠,影子要移動相對的距離 4 | 5 | - 功能 6 | - 移動滑鼠,文字的影子要移動 7 | - 畫面 8 | - 影子要隨著滑鼠相對的位子來修改距離 9 | - 追加: 模糊程度要隨著滑鼠相對的位子來修改 10 | 11 | # HTML 12 | 13 | ## Attribute 14 | - [`contenteditable`](https://www.w3schools.com/tags/att_global_contenteditable.asp) 可編輯段落 15 | 16 | # ES6語法 17 | 18 | [JavaScript ES6 中的物件解構賦值(object destructuring)](https://pjchender.blogspot.tw/2017/01/es6-object-destructuring.html) 19 | 20 | 下面的例子,就是右邊的物件屬性賦值到左邊的物件屬性。 21 | 22 | 對應「物件屬性名稱」。 23 | ```javascript= 24 | const {a: a1, b: b1} = {a: a2, b: b2}; 25 | ``` 26 | 等同於 27 | ```javascript= 28 | const a1 = a2; 29 | const b1 = b2; 30 | ``` 31 | 32 | # Javascript 33 | 34 | ## [`text-shadow`](https://www.w3schools.com/jsref/prop_style_textshadow.asp) 35 | 36 | 用Javascript修改CSS,就要改element.style物件屬性 37 | ```javascript= 38 | object.style.textShadow = "none|h-shadow v-shadow blur color|initial|inherit" 39 | ``` 40 | 41 | ## Mouse Event Coordinate 42 | 43 | [測試小頁面](https://dwatow.github.io/coordinate.html) 44 | 45 | - [movementX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX), movementY 46 | - 距離上次Event觸發時滑鼠位置的差距 47 | - 原點: 上次的 screen(x, y) 48 | - 計算方式: currentEvent.movementX = currentEvent.screenX - previousEvent.screenX. 49 | - [clientX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX), clientY 50 | - application client area的座標, 也就是client的可視區座標 51 | - 可視區左上角 52 | - [offsetX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX), offsetY 53 | - 在HTML元素上的座標 54 | - 原點: 該HTML元素的左上角。 55 | - [pageX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX), pageY 56 | - 在頁面上的位置 57 | - 原點: 該頁的左上角(捲軸捲動時,座標會改變) 58 | - [screenX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX), screenY 59 | - 在螢幕上的座標 60 | - 原點: 作業系統畫面(螢幕)的左上角 61 | - [x](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/x), y 62 | - client(x, y)的意思, 它只是別名 63 | 64 | ## 座標轉換 65 | 66 | 在此有一個盲點,就是`width=clientWidth` 67 | 花了一點時間弄懂...@@ 68 | 69 | ```javascript 70 | const xWalk = Math.round((x / width * walk) - (walk / 2)); 71 | const yWalk = Math.round((y / height * walk) - (walk / 2)); 72 | ``` 73 | -------------------------------------------------------------------------------- /16 Mouse Move Shadow/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mouse Shadow 6 | 7 | 8 | 9 |
10 |

🔥WOAH!

11 |
12 | 13 | 37 | 38 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /16 Mouse Move Shadow/original/index-finished.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mouse Shadow 6 | 7 | 8 | 9 |
10 |

🔥WOAH!

11 |
12 | 13 | 37 | 38 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /16 Mouse Move Shadow/original/index-start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mouse Shadow 6 | 7 | 8 | 9 |
10 |

🔥WOAH!

11 |
12 | 13 | 36 | 37 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /17 Sort Without Articles/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天我們要使用`Array.sort()` 4 | 排序一個band names的Array,但是排序不考慮"The," "An,", "A"這些字眼 5 | 6 | - 功能 7 | - 排序不考慮"The," "An,", "A"這些字眼 8 | - 畫面 9 | - 顯示排序結果 10 | 11 | # Javascript 12 | 13 | ## String object 14 | 15 | [`string.trim()`](https://www.w3schools.com/jsref/jsref_trim_string.asp), Remove whitespace 16 | 17 | ## 比較相似函數名稱 18 | 19 | ### String 20 | 21 | - [`string.split(separator, limit)`](https://www.w3schools.com/jsref/jsref_split.asp) 22 | - 回傳新`array`, `string`用`separator`切割成`array`, 執行完`separator`次結束動作。 23 | - 直接`toString()`等同於用`','`取代`separator` 24 | - [`string.slice(start, end)`](https://www.w3schools.com/jsref/jsref_slice_string.asp) 25 | - 回傳新`string`, `string`切成子`string` 26 | 27 | ### Array 28 | 29 | - [`array.splice(index, howmany, item1, ....., itemX)`](https://www.w3schools.com/jsref/jsref_splice.asp) 30 | - 回傳新`array`, 從`index`移除`homany`個, 後面加上`item1`...`itemX` 31 | - [`array.slice(start, end)`](https://www.w3schools.com/jsref/jsref_slice_array.asp) 32 | - 回傳新`array`, `array`切成子`array` 33 | -------------------------------------------------------------------------------- /17 Sort Without Articles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sort Without Articles 6 | 7 | 8 | 9 | 45 | 46 | 48 | 49 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /17 Sort Without Articles/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sort Without Articles 6 | 7 | 8 | 9 | 43 | 44 | 45 | 46 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /17 Sort Without Articles/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sort Without Articles 6 | 7 | 8 | 9 | 43 | 44 | 45 | 46 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /18 Adding Up Times with Reduce/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天我們要解決一些實際的問題,那就是把播放清單中的時間加起來。 4 | 5 | - 功能 6 | - 加總時間 7 | - 畫面 8 | - 依時間格式顯示`時:分:秒` 9 | 10 | # Javascript 11 | 12 | `map()`連擊!!! 13 | 可以一直將Array的元素轉成別的東西。 14 | 15 | ```javascript 16 | Array.map().map().map(); 17 | ``` 18 | -------------------------------------------------------------------------------- /19 Webcam Fun/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 使用Webcam來進行一些動作 4 | 5 | - 功能 6 | - 拍照功能 7 | - 拍照檔案儲存.png檔 8 | - 拉動上面的range會改變濾鏡參數 9 | - 畫面 10 | - 右上角出現webcam的畫面 11 | - 中間的大圖是`canvas`輸出結果 12 | - 出現照片縮圖列表 13 | - 加上濾鏡(redEffect、rgbSplit、globalAlpha、Green screen) 14 | 15 | > 這一關,跳關了QQ 16 | > 下面有寫原因 17 | 18 | ## 蓋一個Web Service 19 | 20 | 原因是需要讓browser連「安全連線」 21 | 有兩個辦法 22 | 1. `https`連線 23 | 2. 連到`localhost` 24 | 25 | > 注意: 用`file://`是不包含在此範圍的 26 | 27 | 在此用node.js,安裝如下 28 | 29 | ### 安裝npm 30 | 31 | nodejs package manager 32 | 33 | 先裝npm 34 | ```bash 35 | $ sudo apt install npm 36 | ``` 37 | 在專案裝npm 38 | ```bash 39 | $ npm install 40 | ``` 41 | 專案資料夾中,就會出現node_modules 42 | 43 | ### 安裝nvm 44 | 45 | nodejs version manager 46 | 47 | [Github](https://github.com/creationix/nvm/blob/master/README.markdown) 48 | 49 | [安裝步驟](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-16-04) 50 | 51 | - 安裝.. 52 | ``` 53 | $ sudo apt-get update 54 | $ sudo apt-get install build-essential libssl-dev 55 | ``` 56 | 57 | - 下載檔案`install.sh`和`install_nvm.sh` 58 | ``` 59 | $ curl -sL https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh -o install_nvm.sh 60 | ``` 61 | - 安裝nvm 62 | ``` 63 | $ bash install_nvm.sh 64 | ``` 65 | 66 | 接下來就可以做這些事 67 | - `nvm ls-remote` 看「目前可以灌的」版號列表 68 | - `nvm ls-remote <版號>` 看「目前可以灌的」版號列表, 過濾版號 69 | - `nvm install <版號>` 指定安裝`node.js`版號 70 | - `nvm use <版號>`切到指定版號 71 | - `node -v`看目前使用的node.js版號 72 | - `nvm ls`列出目前已安裝的node.js版號 73 | 74 | `express`是`node.js`的framework 75 | 76 | ## Webcam 77 | 78 | 在此,我使用的webcam是用usb接上去的 79 | 用Chrome抓不到,但是用firefox可以抓到 80 | 但是firefox也運作不了。QQ 81 | 82 | ## 無法運作的原因 83 | 84 | `getUserMedia()`依然回傳primse, 但是它的`then`並不會執行,所以抓不到`localMesiaStream`這個東西。QQ 85 | 86 | ```javascript= 87 | function getVideo() { 88 | navigator.mediaDevices.getUserMedia({ 89 | video: true, 90 | audio: false 91 | }) 92 | .then(localMediaStream => { 93 | cnosole.log(localMediaStream); //this line can't run 94 | video.src = window.URL.cereateObjectURL(localMediaStream); 95 | video.play(); 96 | }); 97 | } 98 | 99 | getVideo(); 100 | 101 | ``` 102 | 103 | 104 | ## Javascript 105 | 106 | - ~~[Window Navigator](https://www.w3schools.com/js/js_window_navigator.asp)~~ 107 | - [The Navigator Object](https://www.w3schools.com/jsref/obj_navigator.asp) 包含瀏覽器訊息(對網頁來說是系統訊息?) 108 | 109 | 110 | 執行這一行,瀏覽器會問你是否要分享攝影機。 111 | 112 | ```javascript= 113 | navigator.mediaDevices.getUserMedia({video: true, audio: false}); //較新的瀏覽器支援語法 114 | navigator.getUserMedia(); //舊語法 115 | ``` 116 | 回傳`Promise`,後面要接`.then()`處理。 117 | -------------------------------------------------------------------------------- /19 Webcam Fun/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Get User Media Code Along! 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 | -------------------------------------------------------------------------------- /19 Webcam Fun/original/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/original/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" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /19 Webcam Fun/original/scripts-FINISHED.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 getVideo() { 8 | navigator.mediaDevices.getUserMedia({ video: true, audio: false }) 9 | .then(localMediaStream => { 10 | console.log(localMediaStream); 11 | video.src = window.URL.createObjectURL(localMediaStream); 12 | video.play(); 13 | }) 14 | .catch(err => { 15 | console.error(`OH NO!!!`, err); 16 | }); 17 | } 18 | 19 | function paintToCanvas() { 20 | const width = video.videoWidth; 21 | const height = video.videoHeight; 22 | canvas.width = width; 23 | canvas.height = height; 24 | 25 | return setInterval(() => { 26 | ctx.drawImage(video, 0, 0, width, height); 27 | // take the pixels out 28 | let pixels = ctx.getImageData(0, 0, width, height); 29 | // mess with them 30 | // pixels = redEffect(pixels); 31 | 32 | pixels = rgbSplit(pixels); 33 | // ctx.globalAlpha = 0.8; 34 | 35 | // pixels = greenScreen(pixels); 36 | // put them back 37 | ctx.putImageData(pixels, 0, 0); 38 | }, 16); 39 | } 40 | 41 | function takePhoto() { 42 | // played the sound 43 | snap.currentTime = 0; 44 | snap.play(); 45 | 46 | // take the data out of the canvas 47 | const data = canvas.toDataURL('image/jpeg'); 48 | const link = document.createElement('a'); 49 | link.href = data; 50 | link.setAttribute('download', 'handsome'); 51 | link.innerHTML = `Handsome Man`; 52 | strip.insertBefore(link, strip.firsChild); 53 | } 54 | 55 | function redEffect(pixels) { 56 | for(let i = 0; i < pixels.data.length; i+=4) { 57 | pixels.data[i + 0] = pixels.data[i + 0] + 200; // RED 58 | pixels.data[i + 1] = pixels.data[i + 1] - 50; // GREEN 59 | pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // Blue 60 | } 61 | return pixels; 62 | } 63 | 64 | function rgbSplit(pixels) { 65 | for(let i = 0; i < pixels.data.length; i+=4) { 66 | pixels.data[i - 150] = pixels.data[i + 0]; // RED 67 | pixels.data[i + 500] = pixels.data[i + 1]; // GREEN 68 | pixels.data[i - 550] = pixels.data[i + 2]; // Blue 69 | } 70 | return pixels; 71 | } 72 | 73 | function greenScreen(pixels) { 74 | const levels = {}; 75 | 76 | document.querySelectorAll('.rgb input').forEach((input) => { 77 | levels[input.name] = input.value; 78 | }); 79 | 80 | for (i = 0; i < pixels.data.length; i = i + 4) { 81 | red = pixels.data[i + 0]; 82 | green = pixels.data[i + 1]; 83 | blue = pixels.data[i + 2]; 84 | alpha = pixels.data[i + 3]; 85 | 86 | if (red >= levels.rmin 87 | && green >= levels.gmin 88 | && blue >= levels.bmin 89 | && red <= levels.rmax 90 | && green <= levels.gmax 91 | && blue <= levels.bmax) { 92 | // take it out! 93 | pixels.data[i + 3] = 0; 94 | } 95 | } 96 | 97 | return pixels; 98 | } 99 | 100 | getVideo(); 101 | 102 | video.addEventListener('canplay', paintToCanvas); 103 | -------------------------------------------------------------------------------- /19 Webcam Fun/original/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 | -------------------------------------------------------------------------------- /19 Webcam Fun/original/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 | -------------------------------------------------------------------------------- /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" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要做語音輸入的功能 4 | 5 | - 功能 6 | - 講話要變文字在畫面上 7 | - 停頓太久要分段 8 | - 畫面 9 | - 講話要變文字在畫面上 10 | - 停頓太久要分段 11 | 12 | # Javascript 13 | 14 | ## SpeechRecognition 是一個很新的東西 15 | [window.SpeechRecognition 教學](http://javascript.ruanyifeng.com/htmlapi/webspeech.html) 16 | 17 | - [`SpeechRecognition.end`](https://developer.mozilla.org/en-US/docs/Web/Events/end_%28SpeechRecognition%29), 此例子,講完麥克風就停了要再度打開。 18 | - [`SpeechRecognitionEvent.results`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionEvent/results) 講的結果 19 | - [`SpeechRecognitionResult.isFinal`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionResult/isFinal) 是否講完一段 20 | - [`SpeechRecognitionAlternative.transcript`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionAlternative/transcript) 語音的辨識內容 21 | 22 | ## 更好的寫法 23 | 24 | 兩個寫法各有好壞。 25 | 26 | 原本這樣寫 27 | ```javascript= 28 | const transcript = [...e.results] 29 | .map(result => result[0]) 30 | .map(result => result.transcript); 31 | console.log(...transcript); 32 | ``` 33 | 也可以這樣寫 34 | ```javascript= 35 | const transcript = [...e.results] 36 | .map(result => result[0]) 37 | .map(result => result.transcript).join(''); 38 | console.log(transcript); 39 | ``` 40 | -------------------------------------------------------------------------------- /20 Speech Detection/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech Detection 6 | 7 | 8 | 9 |
10 |
11 | 12 | 47 | 48 | 49 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /20 Speech Detection/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech Detection 6 | 7 | 8 | 9 |
10 |
11 | 12 | 43 | 44 | 45 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /20 Speech Detection/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech Detection 6 | 7 | 8 | 9 |
10 |
11 | 12 | 17 | 18 | 19 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /20 Speech Detection/original/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" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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 --server --files '*.css, *.html, *.js'" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "browser-sync": "^2.12.5" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /21 Geolocation/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要做地理資訊的功能 4 | 5 | - 功能 6 | - 抓到指北角度要改畫面 7 | - 抓到移動速度要更新畫面 8 | - 畫面 9 | - 指北轉要旋轉 10 | - 顯示移動速度 11 | 12 | # Javascript 13 | 14 | - `window.navigator` 15 | - `navigator.geolocation` 16 | - `geolocation.watchPosition()` 監看目前位置 17 | 18 | 19 | - `watchPosition(okfun, errfun)` 20 | - okfun 執行正常有抓到GPS的資訊 21 | - errfun 執行錯誤情況、使用者不授權時的行為 22 | 23 | ```javascript= 24 | function okfun(data) { 25 | console.log(data) 26 | } 27 | ``` 28 | 29 | data是Geoposition物件。 30 | - `timestamp` 當下時間 31 | - `coords` 目前地理資訊 32 | - `accuracy` 平面精度 33 | - `altitude` 高度 34 | - `altitudeAccuracy` 高空精度 35 | - `heading` 真北角度 36 | - `latitude` 緯度 37 | - `longitude` 經度 38 | - `speed` 速度 39 | 40 | ## 問題 41 | 42 | 電腦沒有模擬器 43 | 所以使用iPad來跑看看 44 | 45 | 不過,watchPosition執行幾次就跳err給我了 46 | 不知道為什麼 47 | 48 | 另外,也沒有速度的資訊也沒有指北的角度 49 | -------------------------------------------------------------------------------- /21 Geolocation/original/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" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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 --server --files '*.css, *.html, *.js' --https" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "browser-sync": "^2.12.5" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /22 Follow Along Link Highlighter/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 這幾週[stripe.com](https://stripe.com/)更新了網站,而且它的導覽列的hover效果很厲害 4 | 5 | - 功能 6 | - 超連結Hover時,白色區塊要出現在該超連結區塊上 7 | - 畫面 8 | - 白色區塊的寬高要符合該超連結區塊 9 | - 滑鼠移開時,不變 10 | - 滑鼠進入另一個超連結區塊時,白色的區塊追上去(不會消失) 11 | 12 | # Javascript 13 | 14 | [`htmlElement.getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) 15 | 取得在可視範圍的方塊(內含的離邊都是距離可視範圍) 16 | 17 | 要用`window.scroll`的座標來修正成`page`的座標 18 | 19 | ## 更好的寫法 20 | 21 | 寫這樣 22 | 用`position: absolution`做定位效果 23 | ```javascript= 24 | highlight.style.top = `${linkCoodrs.top}px`; 25 | highlight.style.left = `${linkCoodrs.left}px`; 26 | ``` 27 | 可以寫成這樣 28 | 用`transform`的位移變化`translate()` 29 | ```javascript= 30 | hightlight.style.transform = `translate(${linkCoodrs.left}px), ${linkCoodrs.top}px)`; 31 | ``` 32 | -------------------------------------------------------------------------------- /22 Follow Along Link Highlighter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 👀👀👀Follow Along Nav 6 | 7 | 8 | 9 | 10 | 19 | 20 |
21 |

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

22 |

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

23 |

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

24 |

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

25 |

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

26 |
27 | 28 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /22 Follow Along Link Highlighter/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 👀👀👀Follow Along Nav 6 | 7 | 8 | 9 | 10 | 19 | 20 |
21 |

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

22 |

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

23 |

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

24 |

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

25 |

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

26 |
27 | 28 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /22 Follow Along Link Highlighter/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 👀👀👀Follow Along Nav 6 | 7 | 8 | 9 | 10 | 19 | 20 |
21 |

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

22 |

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

23 |

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

24 |

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

25 |

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

26 |
27 | 28 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /22 Follow Along Link Highlighter/original/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | *, *:before, *:after { 5 | box-sizing: inherit; 6 | } 7 | body { 8 | min-height: 100vh; 9 | margin: 0; /* Important! */ 10 | font-family: sans-serif; 11 | background: 12 | linear-gradient(45deg, hsla(340, 100%, 55%, 1) 0%, hsla(340, 100%, 55%, 0) 70%), 13 | 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%), 15 | linear-gradient(315deg, hsla(35, 95%, 55%, 1) 100%, hsla(35, 95%, 55%, 0) 70%); 16 | } 17 | 18 | .wrapper { 19 | margin:0 auto; 20 | max-width:500px; 21 | font-size: 20px; 22 | line-height: 2; 23 | position: relative; 24 | } 25 | 26 | a { 27 | text-decoration: none; 28 | color:black; 29 | background:rgba(0,0,0,0.05); 30 | border-radius: 20px 31 | } 32 | 33 | .highlight { 34 | transition: all 0.2s; 35 | border-bottom:2px solid white; 36 | position: absolute; 37 | top:0; 38 | background:white; 39 | left:0; 40 | z-index: -1; 41 | border-radius:20px; 42 | display: block; 43 | box-shadow: 0 0 10px rgba(0,0,0,0.2) 44 | } 45 | 46 | .menu { 47 | padding: 0; 48 | display: flex; 49 | list-style: none; 50 | justify-content: center; 51 | margin:100px 0; 52 | } 53 | 54 | .menu a { 55 | display: inline-block; 56 | padding:5px; 57 | margin:0 20px; 58 | color:black; 59 | } 60 | -------------------------------------------------------------------------------- /22 Follow Along Link Highlighter/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | *, *:before, *:after { 5 | box-sizing: inherit; 6 | } 7 | body { 8 | min-height: 100vh; 9 | margin: 0; /* Important! */ 10 | font-family: sans-serif; 11 | background: 12 | linear-gradient(45deg, hsla(340, 100%, 55%, 1) 0%, hsla(340, 100%, 55%, 0) 70%), 13 | 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%), 15 | linear-gradient(315deg, hsla(35, 95%, 55%, 1) 100%, hsla(35, 95%, 55%, 0) 70%); 16 | } 17 | 18 | .wrapper { 19 | margin:0 auto; 20 | max-width:500px; 21 | font-size: 20px; 22 | line-height: 2; 23 | position: relative; 24 | } 25 | 26 | a { 27 | text-decoration: none; 28 | color:black; 29 | background:rgba(0,0,0,0.05); 30 | border-radius: 20px 31 | } 32 | 33 | .highlight { 34 | display: block; 35 | 36 | position: absolute; 37 | top:0; 38 | left:0; 39 | z-index: -1; 40 | 41 | border-bottom:2px solid white; 42 | border-radius:20px; 43 | 44 | background:white; 45 | 46 | box-shadow: 0 0 10px rgba(0,0,0,0.2); 47 | transition: all 0.1s; 48 | } 49 | 50 | .menu { 51 | padding: 0; 52 | display: flex; 53 | list-style: none; 54 | justify-content: center; 55 | margin:100px 0; 56 | } 57 | 58 | .menu a { 59 | display: inline-block; 60 | padding:5px; 61 | margin:0 20px; 62 | color:black; 63 | } 64 | -------------------------------------------------------------------------------- /23 Speech Synthesis/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天要做語音輸出(speech synthesis) 4 | 5 | - 功能 6 | - 切換不同的聲音(語系) 7 | - 調整說話速度(聲調改變) 8 | - 調整咬字速度 9 | - 要能輸入要唸的內容 10 | - 觸發開始講 11 | - 觸發閉嘴 12 | - 切換聲音時,會閉嘴再開始講 13 | - 畫面 14 | - 好像沒什麼特別的 15 | 16 | # Javascript 17 | 18 | ## 語音訊息 19 | ```javascript= 20 | const msg = new SpeechSynthesisUtterance(); 21 | ``` 22 | `msg.text` 要唸的文字 23 | `msg.voice` 要用什麼語音服務唸 24 | 25 | ## 語音服務物件 26 | 27 | 等一下他要唸語音訊息 28 | 29 | - `speechSynthesis.getVoices();` 目前可以用的語音服務 30 | - `speechSynthesis.speak(msg);`唸 31 | - `speechSynthesis.cancel();` 停!別唸了 32 | 33 | ## 較好的寫法 34 | 35 | 有一個function要丟參數,但是又在只能丟函數參考的地方 36 | 無法連同參數一起丟進去時 37 | 38 | 寫一個新的function 39 | ```javascript= 40 | function toggle_false() { 41 | toggle(false); 42 | } 43 | stopButton.addEventListener('click', toggle_false); 44 | ``` 45 | 46 | 用bind() 47 | ```javascript= 48 | stopButton.addEventListener('click', toggle.bind(null, false)); 49 | ``` 50 | 51 | 用arrow function 52 | ```javascript= 53 | stopButton.addEventListener('click', () => toggle(false)); 54 | ``` 55 | -------------------------------------------------------------------------------- /23 Speech Synthesis/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech Synthesis 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |

The Voiceinator 5000

14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /23 Speech Synthesis/npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/home/chris/.nvm/versions/node/v6.10.2/bin/node', 3 | 1 verbose cli '/home/chris/.nvm/versions/node/v6.10.2/bin/npm', 4 | 1 verbose cli 'start' ] 5 | 2 info using npm@3.10.10 6 | 3 info using node@v6.10.2 7 | 4 verbose stack Error: ENOENT: no such file or directory, open '/home/chris/code/JavaScript30/23 Speech Synthesis/package.json' 8 | 4 verbose stack at Error (native) 9 | 5 verbose cwd /home/chris/code/JavaScript30/23 Speech Synthesis 10 | 6 error Linux 4.4.0-72-generic 11 | 7 error argv "/home/chris/.nvm/versions/node/v6.10.2/bin/node" "/home/chris/.nvm/versions/node/v6.10.2/bin/npm" "start" 12 | 8 error node v6.10.2 13 | 9 error npm v3.10.10 14 | 10 error path /home/chris/code/JavaScript30/23 Speech Synthesis/package.json 15 | 11 error code ENOENT 16 | 12 error errno -2 17 | 13 error syscall open 18 | 14 error enoent ENOENT: no such file or directory, open '/home/chris/code/JavaScript30/23 Speech Synthesis/package.json' 19 | 15 error enoent ENOENT: no such file or directory, open '/home/chris/code/JavaScript30/23 Speech Synthesis/package.json' 20 | 15 error enoent This is most likely not a problem with npm itself 21 | 15 error enoent and is related to npm not being able to find a file. 22 | 16 verbose exit [ -2, true ] 23 | -------------------------------------------------------------------------------- /23 Speech Synthesis/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech Synthesis 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |

The Voiceinator 5000

14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /23 Speech Synthesis/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speech Synthesis 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |

The Voiceinator 5000

14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /23 Speech Synthesis/original/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 | -------------------------------------------------------------------------------- /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/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 4 | - 功能 5 | - menu到畫面最上面,要固定 6 | - 畫面 7 | - menu固定在畫面上面時,要露出Logo 8 | - menu不固定時,不露出logo 9 | - menu固定的那一瞬間,下面的文章不可以出現跳動 10 | 11 | # Javascript 12 | 13 | ## 另一種改法 14 | 15 | 得到一個想法 16 | 17 | 「新增`.style`若太麻煩」(太多code) 18 | 不如「新增`.classList`」 19 | 20 | 由於在此不只是要加上去,還要移除,所以,用`classList`的操作會比操作`style`來得容易又好懂 21 | -------------------------------------------------------------------------------- /24 Sticky Nav/original/style-FINISHED.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 | *, *:before, *:after { 12 | box-sizing: inherit; 13 | } 14 | 15 | .site-wrap { 16 | max-width: 700px; 17 | margin: 70px auto; 18 | background:white; 19 | padding:40px; 20 | text-align: justify; 21 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05); 22 | transform: scale(0.98); 23 | transition: transform 0.5s; 24 | } 25 | 26 | body.fixed-nav .site-wrap { 27 | transform: scale(1); 28 | } 29 | 30 | 31 | header { 32 | text-align: center; 33 | height:50vh; 34 | background:url(http://wes.io/iEgP/wow-so-deep.jpg) bottom center no-repeat; 35 | background-size:cover; 36 | display:flex; 37 | align-items:center; 38 | justify-content: center; 39 | } 40 | 41 | h1 { 42 | color:white; 43 | font-size: 7vw; 44 | text-shadow: 3px 4px 0 rgba(0,0,0,0.2) 45 | } 46 | 47 | nav { 48 | background:black; 49 | top:0; 50 | width: 100%; 51 | transition:all 0.5s; 52 | position: relative; 53 | z-index: 1; 54 | } 55 | 56 | body.fixed-nav nav { 57 | position: fixed; 58 | box-shadow:0 5px 0 rgba(0,0,0,0.1); 59 | } 60 | 61 | nav ul { 62 | margin: 0; 63 | padding:0; 64 | list-style: none; 65 | display:flex; 66 | } 67 | 68 | nav li { 69 | flex:1; 70 | text-align: center; 71 | display: flex; 72 | justify-content: center; 73 | align-items: center; 74 | } 75 | 76 | li.logo { 77 | max-width:0; 78 | overflow: hidden; 79 | background: white; 80 | transition: all 0.5s; 81 | font-weight: 600; 82 | font-size: 30px; 83 | } 84 | 85 | li.logo a { 86 | color:black; 87 | } 88 | 89 | .fixed-nav li.logo { 90 | max-width:500px; 91 | } 92 | 93 | nav a { 94 | text-decoration: none; 95 | padding:20px; 96 | display: inline-block; 97 | color:white; 98 | transition:all 0.2s; 99 | text-transform: uppercase; 100 | } 101 | -------------------------------------------------------------------------------- /24 Sticky Nav/original/style-START.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 | *, *:before, *:after { 12 | box-sizing: inherit; 13 | } 14 | 15 | .site-wrap { 16 | max-width: 700px; 17 | margin: 70px auto; 18 | background:white; 19 | padding:40px; 20 | text-align: justify; 21 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05); 22 | transform: scale(0.98); 23 | transition: transform 0.5s; 24 | } 25 | 26 | header { 27 | text-align: center; 28 | height:50vh; 29 | background:url(http://wes.io/iEgP/wow-so-deep.jpg) bottom center no-repeat; 30 | background-size:cover; 31 | display:flex; 32 | align-items:center; 33 | justify-content: center; 34 | } 35 | 36 | h1 { 37 | color:white; 38 | font-size: 7vw; 39 | text-shadow: 3px 4px 0 rgba(0,0,0,0.2) 40 | } 41 | 42 | nav { 43 | background:black; 44 | top:0; 45 | width: 100%; 46 | transition:all 0.5s; 47 | position: relative; 48 | z-index: 1; 49 | } 50 | 51 | nav ul { 52 | margin: 0; 53 | padding:0; 54 | list-style: none; 55 | display:flex; 56 | } 57 | 58 | nav li { 59 | flex:1; 60 | text-align: center; 61 | display: flex; 62 | justify-content: center; 63 | align-items: center; 64 | } 65 | 66 | li.logo { 67 | max-width:0; 68 | overflow: hidden; 69 | background: white; 70 | transition: all .5s; 71 | font-weight: 600; 72 | font-size: 30px; 73 | } 74 | 75 | li.logo a { 76 | color:black; 77 | } 78 | 79 | nav a { 80 | text-decoration: none; 81 | padding:20px; 82 | display: inline-block; 83 | color:white; 84 | transition:all 0.2s; 85 | text-transform: uppercase; 86 | } 87 | -------------------------------------------------------------------------------- /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 | *, *:before, *:after { 12 | box-sizing: inherit; 13 | } 14 | 15 | .site-wrap { 16 | max-width: 700px; 17 | margin: 70px auto; 18 | background:white; 19 | padding:40px; 20 | text-align: justify; 21 | box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05); 22 | transform: scale(0.98); 23 | transition: transform 0.5s; 24 | } 25 | 26 | .fixed-nav .site-wrap { 27 | /*transform: scale(1.02);*/ 28 | } 29 | 30 | header { 31 | text-align: center; 32 | height:50vh; 33 | background:url(http://wes.io/iEgP/wow-so-deep.jpg) bottom center no-repeat; 34 | background-size:cover; 35 | display:flex; 36 | align-items:center; 37 | justify-content: center; 38 | } 39 | 40 | h1 { 41 | color:white; 42 | font-size: 7vw; 43 | text-shadow: 3px 4px 0 rgba(0,0,0,0.2) 44 | } 45 | 46 | nav { 47 | background:black; 48 | top:0; 49 | width: 100%; 50 | transition:all 0.5s; 51 | position: relative; 52 | z-index: 1; 53 | } 54 | 55 | .fixed-nav nav { 56 | position: fixed; 57 | box-shadow: 0 5px rgba(0, 0, 0, 0.1); 58 | } 59 | 60 | nav ul { 61 | margin: 0; 62 | padding:0; 63 | list-style: none; 64 | display:flex; 65 | } 66 | 67 | nav li { 68 | flex:1; 69 | text-align: center; 70 | display: flex; 71 | justify-content: center; 72 | align-items: center; 73 | } 74 | 75 | li.logo { 76 | max-width: 0; 77 | overflow: hidden; 78 | background: white; 79 | transition: all .5s; 80 | font-weight: 600; 81 | font-size: 30px; 82 | } 83 | 84 | .fixed-nav li.logo { 85 | max-width: 500px; 86 | } 87 | 88 | li.logo a { 89 | color:black; 90 | } 91 | 92 | nav a { 93 | text-decoration: none; 94 | padding:20px; 95 | display: inline-block; 96 | color:white; 97 | transition:all 0.2s; 98 | text-transform: uppercase; 99 | } 100 | -------------------------------------------------------------------------------- /25 Event Capture, Propagation, Bubbling and Once/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 這一週,不做漂亮的東西。來細說`addEventListener`吧。 4 | 5 | 跟著做,就知道了。 6 | 7 | # Javascript 8 | 9 | 細說`addEventListener` 10 | 11 | ## [`addEventListener`](https://www.w3schools.com/jsref/met_element_addeventlistener.asp) 12 | 13 | 語法: 14 | ```javascript= 15 | element.addEventListener(event, function, useCapture) 16 | ``` 17 | `useCapture`: `true` | `false` 18 | 19 | 四個重點 20 | 1. Event Capture 21 | 1. Propagation 22 | 1. Bubbling 23 | 1. Once(new property) 24 | 25 | ## 名詞定義 26 | - `bubbling` 冒泡(像泡泡在水裡浮上來的過程) 27 | - operator system -> browser application -> tab -> html -> body到你按的element, 28 | 都會被觸發事件。 29 | - 事件預設的執行順序是從element往上到body... 30 | - Capture 捕獲 31 | - browser做的事稱為Capture 32 | - 意思是你點擊element, 以top down的順序capture所有的event 33 | - `event.stopPropagation()` 34 | - 取消預設(bubbling)行為,變成「點到誰,就是誰」 35 | 36 | ## 舉例 37 | 38 | 以這個例子的HTML結構來說。 39 | 40 | ### 預設行為: `bobble up` 41 | 如果每一個都用這種方式加入event 42 | ```javascript 43 | addEventListener('click', fun); 44 | ``` 45 | capture時,執行fun的順序將會是由上而下(DOM樹的樹梢到樹根) 46 | ``` 47 | tree 48 | two 49 | one 50 | ``` 51 | ### `capture:true` 52 | 在`addEventListener`的第三個參數加上這樣的物件 53 | `capture: false` 會將行預設行為 54 | ```javascript 55 | addEventListener('click', fun, {capture:true}); 56 | ``` 57 | capture時,執行fun的順序將會是由下而上(DOM樹的根到樹梢) 58 | ``` 59 | one 60 | two 61 | tree 62 | ``` 63 | ### `stop propagation` 64 | 65 | 在監聽觸發function裡面加上`e.stopPropagation()` 66 | 意思是"stop bubbling this event up. I clicked the one that I actually wanted." 67 | 68 | ```javascript 69 | function fun (e){ 70 | e.stopPropagation(); //停止漣漪效應 71 | //other thing... 72 | } 73 | addEventListener('click', fun); 74 | ``` 75 | 要設定`capture:false`才是點擊誰,觸發誰 76 | ``` 77 | three 78 | ``` 79 | 若設定`capture:true`是另一個順序 80 | ``` 81 | one 82 | ``` 83 | ### 只監聽一次 84 | 在`addEventListener`的第三個參數加上這樣的物件 85 | `once: false` 會將行預設行為 86 | ```javascript 87 | addEventListener('click', fun, {once:true}); 88 | ``` 89 | capture之後,就自動執行`removeEventListener()` 90 | ``` 91 | one 92 | two 93 | tree 94 | ``` 95 | 96 | ## 延伸閱讀 97 | - [Bubbling and capturing](https://javascript.info/bubbling-and-capturing) 98 | -------------------------------------------------------------------------------- /25 Event Capture, Propagation, Bubbling and Once/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Understanding JavaScript's Capture 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 | 45 | 46 | 49 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /25 Event Capture, Propagation, Bubbling and Once/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Understanding JavaScript's Capture 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 | 39 | 40 | 41 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /25 Event Capture, Propagation, Bubbling and Once/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Understanding JavaScript's Capture 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 | 39 | 40 | 41 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /26 Stripe Follow Along Nav/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 再看一次[stripe.com](https://stripe.com/)網站,而且它的導覽列的hover效果很厲害 4 | 5 | 這次來做做它的效果吧! 6 | 7 | - 功能 8 | - 超連結Hover時,白色區塊要出現在該超連結的下拉區塊 9 | - 畫面 10 | - 白色區塊的寬高要符合該超連結的下拉區塊 11 | - 滑鼠移開時,白色區塊要消失 12 | - 白色區塊出現,內容才出現 13 | - 下拉區塊尚未完全出現,滑鼠移動時,要取消顯示內容 14 | 15 | # Javascript 16 | 17 | `element.nextElementSibling()` 18 | 下一個element(等同於css selector的`+`) 19 | 20 | 做hover的效果時 21 | 「消失/出現」和「漸漸消失/漸漸出現」的css要分開 22 | ```= 23 | .trigger-enter { 24 | display: block; 25 | } 26 | 27 | .trigger-enter-active { 28 | opacity: 1; 29 | } 30 | ``` 31 | 32 | 這樣下面才會抓得到東西 33 | ```javascript= 34 | element.getBoundingClientRect(); 35 | -------------------------------------------------------------------------------- /27 Click and Drag/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 今天來實現拖拉互動的功能。 4 | 5 | - 功能 6 | - 拖拉互動的功能, 要有拖拉畫面的感覺 7 | - 畫面 8 | - 拖拉的程度,要和畫面移動程度,相同的pixel 9 | 10 | 11 | [`preventDefault()`](https://www.w3schools.com/jsref/event_preventdefault.asp), 取消預設行為 12 | 在此例,好像沒什麼太大明顯的效果 13 | 14 | ## 以View寬度為滑動寬度 15 | ```javascript= 16 | items.addEventListener('mousemove', scrolling); 17 | function scrolling (e) { 18 | if (mouseDown) { 19 | const itemsRect = this.getBoundingClientRect(); 20 | this.scrollLeft -= (e.movementX/itemsRect.width)*this.scrollWidth; 21 | } 22 | } 23 | ``` 24 | ## 以滑鼠移動寬度(pixel數)為滑動寬度 25 | - 滑鼠點下時 26 | - 記下目前scroll的絕對位置 27 | - 記下游標的位置 28 | - 移動滑鼠時 29 | - 計算滑鼠移動的相對位置 30 | - 將相對位置加到scroll的絕對位置 31 | - 不可以在此記scroll的絕對位置,會因為scroll移動而變化!!! 32 | ```javascript= 33 | items.addEventListener('mousemove', scrolling); 34 | function scrolling (e) { 35 | if (!mouseDown) return 36 | items.scrollLeft = (startX - e.pageX) + startScrollLeft; 37 | } 38 | items.addEventListener('mousedown', (e) => { 39 | mouseDown = true; 40 | items.classList.add('active'); 41 | startX = e.pageX 42 | startScrollLeft = items.scrollLeft; 43 | }); 44 | ``` 45 | -------------------------------------------------------------------------------- /27 Click and Drag/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Click and Drag 6 | 7 | 8 | 9 |
10 |
01
11 |
02
12 |
03
13 |
04
14 |
05
15 |
06
16 |
07
17 |
08
18 |
09
19 |
10
20 |
11
21 |
12
22 |
13
23 |
14
24 |
15
25 |
16
26 |
17
27 |
18
28 |
19
29 |
20
30 |
21
31 |
22
32 |
23
33 |
24
34 |
25
35 |
36 | 37 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /27 Click and Drag/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Click and Drag 6 | 7 | 8 | 9 |
10 |
01
11 |
02
12 |
03
13 |
04
14 |
05
15 |
06
16 |
07
17 |
08
18 |
09
19 |
10
20 |
11
21 |
12
22 |
13
23 |
14
24 |
15
25 |
16
26 |
17
27 |
18
28 |
19
29 |
20
30 |
21
31 |
22
32 |
23
33 |
24
34 |
25
35 |
36 | 37 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /27 Click and Drag/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Click and Drag 6 | 7 | 8 | 9 |
10 |
01
11 |
02
12 |
03
13 |
04
14 |
05
15 |
06
16 |
07
17 |
08
18 |
09
19 |
10
20 |
11
21 |
12
22 |
13
23 |
14
24 |
15
25 |
16
26 |
17
27 |
18
28 |
19
29 |
20
30 |
21
31 |
22
32 |
23
33 |
24
34 |
25
35 |
36 | 37 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /27 Click and Drag/original/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 | -------------------------------------------------------------------------------- /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/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 做一個Range改變影片的播放速度 4 | 5 | - 功能 6 | - 拖拉Range, 最小值不可以設0.4 7 | - 拖拉Range, 最大值不可以設4 8 | - 畫面 9 | - 拖拉Range, 顯示目前的速度倍率,顯示格式: nx(n是整數) 10 | 11 | # Javascript 12 | 13 | 百分比的寫法 14 | 15 | ```javascript= 16 | height = Math.round(percent * 100) + '%' 17 | playbackRate = percent * (max - min) + min; 18 | ``` 19 | 20 | 不過我覺得,不如寫一個if卡住調整的幅度 21 | ```javascript= 22 | const y = (e.offsetY / speedMaxY); 23 | if (y < 0.08) return; 24 | video.playbackRate = y * 4; //0.4~4 25 | ``` 26 | -------------------------------------------------------------------------------- /28 Video Speed Controller/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Video Speed Scrubber 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |
15 |
16 | 17 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /28 Video Speed Controller/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Video Speed Scrubber 6 | 7 | 8 | 9 | 10 |
11 | 12 |

13 |

14 |
15 |
16 |
17 | 18 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /28 Video Speed Controller/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Video Speed Scrubber 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |
15 |
16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /28 Video Speed Controller/original/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 | -------------------------------------------------------------------------------- /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/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 做一個倒數計時器 4 | 5 | - 功能 6 | - 各種設定好的時間,進行倒數 7 | - 自訂義分鐘倒數 8 | - 開新的倒數,直接取消上一次 9 | - 畫面 10 | - 顯示目前倒數的數字 11 | - 顯示倒數歸零時,是幾點幾分 12 | 13 | # Javascript 14 | 15 | 倒數計時 16 | 5分鐘 17 | 15分鐘 18 | 19 | 20 | 時間 和數字運算,會當成ms進行運算 21 | 22 | 停止計時 23 | ```javascript= 24 | const pid = setInterval() 25 | clearInterval(pid); 26 | ``` 27 | 28 | ~~範例程式碼的`timer`視為閉包~~ 29 | 30 | 31 | 忽略預設行為 32 | ```javascript= 33 | e.preventDefault(); 34 | ``` 35 | -------------------------------------------------------------------------------- /29 Countdown Timer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Countdown Timer 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 | -------------------------------------------------------------------------------- /29 Countdown Timer/original/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Countdown Timer 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 | -------------------------------------------------------------------------------- /29 Countdown Timer/original/scripts-FINISHED.js: -------------------------------------------------------------------------------- 1 | let countdown; 2 | const timerDisplay = document.querySelector('.display__time-left'); 3 | const endTime = document.querySelector('.display__end-time'); 4 | const buttons = document.querySelectorAll('[data-time]'); 5 | 6 | function timer(seconds) { 7 | // clear any existing timers 8 | clearInterval(countdown); 9 | 10 | const now = Date.now(); 11 | const then = now + seconds * 1000; 12 | displayTimeLeft(seconds); 13 | displayEndTime(then); 14 | 15 | countdown = setInterval(() => { 16 | const secondsLeft = Math.round((then - Date.now()) / 1000); 17 | // check if we should stop it! 18 | if(secondsLeft < 0) { 19 | clearInterval(countdown); 20 | return; 21 | } 22 | // display it 23 | displayTimeLeft(secondsLeft); 24 | }, 1000); 25 | } 26 | 27 | function displayTimeLeft(seconds) { 28 | const minutes = Math.floor(seconds / 60); 29 | const remainderSeconds = seconds % 60; 30 | const display = `${minutes}:${remainderSeconds < 10 ? '0' : '' }${remainderSeconds}`; 31 | document.title = display; 32 | timerDisplay.textContent = display; 33 | } 34 | 35 | function displayEndTime(timestamp) { 36 | const end = new Date(timestamp); 37 | const hour = end.getHours(); 38 | const adjustedHour = hour > 12 ? hour - 12 : hour; 39 | const minutes = end.getMinutes(); 40 | endTime.textContent = `Be Back At ${adjustedHour}:${minutes < 10 ? '0' : ''}${minutes}`; 41 | } 42 | 43 | function startTimer() { 44 | const seconds = parseInt(this.dataset.time); 45 | timer(seconds); 46 | } 47 | 48 | buttons.forEach(button => button.addEventListener('click', startTimer)); 49 | document.customForm.addEventListener('submit', function(e) { 50 | e.preventDefault(); 51 | const mins = this.minutes.value; 52 | console.log(mins); 53 | timer(mins * 60); 54 | this.reset(); 55 | }); 56 | -------------------------------------------------------------------------------- /29 Countdown Timer/original/scripts-START.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwatow/JavaScript30/80d7b5b634ecad79002b2f926d576fdf4a791d36/29 Countdown Timer/original/scripts-START.js -------------------------------------------------------------------------------- /29 Countdown Timer/original/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 | -------------------------------------------------------------------------------- /29 Countdown Timer/scripts.js: -------------------------------------------------------------------------------- 1 | const timers = document.querySelectorAll('.timer__button'); 2 | const show_end_time = document.querySelector('.display__end-time'); 3 | const show_remaining_time = document.querySelector('.display__time-left'); 4 | 5 | document.customForm.addEventListener('submit', function (e) { 6 | e.preventDefault(); 7 | const sec_time = this.minutes.value * 60 * 1000; 8 | const now = new Date(); 9 | end_time = new Date(now.getTime() + sec_time); 10 | 11 | const diff = new Date(sec_time); 12 | show_end_time.textContent = `倒數到: ${end_time.getHours()}:${end_time.getMinutes()}:${end_time.getSeconds()}`; 13 | 14 | clearInterval(timer_pid); 15 | timer_pid = setInterval(countDownTimer, 500); 16 | 17 | 18 | this.reset(); 19 | }); 20 | 21 | 22 | 23 | end_time = new Date(); 24 | 25 | let timer_pid; 26 | // const custumer_timer = document.querySelector('.customForm'); 27 | 28 | function countDownTimer() { 29 | const now = new Date(); 30 | const remaining_time = new Date(end_time - now); 31 | show_remaining_time.textContent = `${remaining_time.getMinutes()}:${remaining_time.getSeconds()}`; 32 | if(remaining_time <= 1) clearInterval(timer_pid); 33 | } 34 | 35 | function setTimer() { 36 | const sec_time = this.dataset.time * 1000; 37 | const now = new Date(); 38 | end_time = new Date(now.getTime() + sec_time); 39 | 40 | const diff = new Date(sec_time); 41 | show_end_time.textContent = `倒數到: ${end_time.getHours()}:${end_time.getMinutes()}:${end_time.getSeconds()}`; 42 | 43 | clearInterval(timer_pid); 44 | timer_pid = setInterval(countDownTimer, 500); 45 | } 46 | 47 | timers.forEach(timer => { 48 | timer.addEventListener('click', setTimer); 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/README.MD: -------------------------------------------------------------------------------- 1 | # 完成目標 2 | 3 | 做一個打地鼠的遊戲 4 | 5 | - 功能 6 | - 一個關卡要幾分鐘要固定 7 | - 地鼠冒出來、自動縮下去的時間間隔要不一樣 8 | - 畫面 9 | - 畫面要顯示目前的關卡 10 | - 地鼠要自動冒出來 11 | - 地鼠要自動縮下去 12 | - 地鼠被點到要縮下去 13 | 14 | # Javascript 15 | 16 | 指定取亂數的範圍 17 | 18 | ```javascript= 19 | function randTime(min, max) { 20 | return Math.round(Math.random() * (max - min) + min); 21 | } 22 | ``` 23 | 24 | 避免重複取亂數 25 | ```javascript= 26 | let lastestId; 27 | function randomHole() { 28 | const id = Math...; 29 | if (id === lastestId) { 30 | return randomHole(holes); 31 | } 32 | eles { 33 | return id; 34 | } 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /30 Whack A Mole/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Whack A Mole! 6 | 7 | 8 | 9 | 10 | 11 |

Whack-a-mole! 0

12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /30 Whack A Mole/original/index-FINISHED.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Whack A Mole! 6 | 7 | 8 | 9 | 10 | 11 |

Whack-a-mole! 0

12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /30 Whack A Mole/original/index-START.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Whack A Mole! 6 | 7 | 8 | 9 | 10 | 11 |

Whack-a-mole! 0

12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /30 Whack A Mole/original/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 | -------------------------------------------------------------------------------- /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 | [Javascript30.com](https://javascript30.com/) [[github]](https://github.com/wesbos/JavaScript30) 4 | 5 | ## 原本的檔案 6 | 7 | 放置在所有課程裡面的`original`的資料夾裡。 8 | 9 | ## 練習內容 10 | 11 | [練習記錄@Github](https://github.com/dwatow/JavaScript30/) 12 | 13 | [練習成果](https://dwatow.github.io/JavaScript30/) 14 | 15 | ## 各課程學習到的新東西 16 | 17 | 將新學到的用法,找到w3school的參考頁面放到readme.md中。 18 | 19 | 並且練習放在index.html 20 | 21 | 1. [JavaScript Drum Kit](https://dwatow.github.io/JavaScript30/01%20JavaScript%20Drum%20Kit) 22 | 2. [JS and CSS Clock](https://dwatow.github.io/JavaScript30/02%20JS%20and%20CSS%20Clock) 23 | 3. [CSS Variables](https://dwatow.github.io/JavaScript30/03%20CSS%20Variables) 24 | 4. [Array Cardio Day 1](https://dwatow.github.io/JavaScript30/04%20Array%20Cardio%20Day%201) 25 | 5. [Flex Panel Gallery](https://dwatow.github.io/JavaScript30/05%20Flex%20Panel%20Gallery) 26 | 6. [Type Ahead](https://dwatow.github.io/JavaScript30/06%20Type%20Ahead) 27 | 7. [Array Cardio Day 2](https://dwatow.github.io/JavaScript30/07%20Array%20Cardio%20Day%202) 28 | 8. [Fun with HTML5 Canvas](https://dwatow.github.io/JavaScript30/08%20Fun%20with%20HTML5%20Canvas) 29 | 9. [Dev Tools Domination](https://dwatow.github.io/JavaScript30/09%20Dev%20Tools%20Domination) 30 | 10. [Hold Shift and Check Checkboxes](https://dwatow.github.io/JavaScript30/10%20Hold%20Shift%20and%20Check%20Checkboxes) 31 | 11. [Custom Video Player](https://dwatow.github.io/JavaScript30/11%20Custom%20Video%20Player) 32 | 12. [Key Sequence Detection](https://dwatow.github.io/JavaScript30/12%20Key%20Sequence%20Detection) 33 | 13. [Slide in on Scroll](https://dwatow.github.io/JavaScript30/13%20Slide%20in%20on%20Scroll) 34 | 14. [JavaScript References VS Copying](https://dwatow.github.io/JavaScript30/14%20JavaScript%20References%20VS%20Copying) 35 | 15. [LocalStorage](https://dwatow.github.io/JavaScript30/15%20LocalStorage) 36 | 16. [Mouse Move Shadow](https://dwatow.github.io/JavaScript30/16%20Mouse%20Move%20Shadow) 37 | 17. [Sort Without Articles](https://dwatow.github.io/JavaScript30/17%20Sort%20Without%20Articles) 38 | 18. [Adding Up Times with Reduce](https://dwatow.github.io/JavaScript30/18%20Adding%20Up%20Times%20with%20Reduce) 39 | 19. [Webcam Fun](https://dwatow.github.io/JavaScript30/19%20Webcam%20Fun) 40 | 20. [Speech Detection](https://dwatow.github.io/JavaScript30/20%20Speech%20Detection) 41 | 21. [Geolocation](https://dwatow.github.io/JavaScript30/21%20Geolocation) 42 | 22. [Follow Along Link Highlighter](https://dwatow.github.io/JavaScript30/22%20Follow%20Along%20Link%20Highlighter) 43 | 23. [Speech Synthesis](https://dwatow.github.io/JavaScript30/23%20Speech%20Synthesis) 44 | 24. [Sticky Nav](https://dwatow.github.io/JavaScript30/24%20Sticky%20Nav) 45 | 25. [Event Capture, Propagation, Bubbling and Once](https://dwatow.github.io/JavaScript30/25%20Event%20Capture%2C%20Propagation%2C%20Bubbling%20and%20Once) 46 | 26. [Stripe Follow Along Nav](https://dwatow.github.io/JavaScript30/26%20Stripe%20Follow%20Along%20Nav) 47 | 27. [Click and Drag](https://dwatow.github.io/JavaScript30/27%20Click%20and%20Drag) 48 | 28. [Video Speed Controller](https://dwatow.github.io/JavaScript30/28%20Video%20Speed%20Controller) 49 | 29. [Countdown Timer](https://dwatow.github.io/JavaScript30/29%20Countdown%20Timer) 50 | 30. [Whack A Mole](https://dwatow.github.io/JavaScript30/30%20Whack%20A%20Mole) 51 | -------------------------------------------------------------------------------- /Readme.MD: -------------------------------------------------------------------------------- 1 | # JavaScript30練習筆記 2 | 3 | [Javascript30.com](https://javascript30.com/) [[github]](https://github.com/wesbos/JavaScript30) 4 | 5 | ## 原本的檔案 6 | 7 | 放置在所有課程裡面的`original`的資料夾裡。 8 | 9 | ## 練習內容 10 | 11 | [練習記錄@Github](https://github.com/dwatow/JavaScript30/) 12 | 13 | [練習成果](https://dwatow.github.io/JavaScript30/) 14 | 15 | ## 各課程學習到的新東西 16 | 17 | 將新學到的用法,找到w3school的參考頁面放到readme.md中。 18 | 19 | 並且練習放在index.html 20 | 21 | 1. [JavaScript Drum Kit](https://dwatow.github.io/JavaScript30/01%20JavaScript%20Drum%20Kit) 22 | 2. [JS and CSS Clock](https://dwatow.github.io/JavaScript30/02%20JS%20and%20CSS%20Clock) 23 | 3. [CSS Variables](https://dwatow.github.io/JavaScript30/03%20CSS%20Variables) 24 | 4. [Array Cardio Day 1](https://dwatow.github.io/JavaScript30/04%20Array%20Cardio%20Day%201) 25 | 5. [Flex Panel Gallery](https://dwatow.github.io/JavaScript30/05%20Flex%20Panel%20Gallery) 26 | 6. [Type Ahead](https://dwatow.github.io/JavaScript30/06%20Type%20Ahead) 27 | 7. [Array Cardio Day 2](https://dwatow.github.io/JavaScript30/07%20Array%20Cardio%20Day%202) 28 | 8. [Fun with HTML5 Canvas](https://dwatow.github.io/JavaScript30/08%20Fun%20with%20HTML5%20Canvas) 29 | 9. [Dev Tools Domination](https://dwatow.github.io/JavaScript30/09%20Dev%20Tools%20Domination) 30 | 10. [Hold Shift and Check Checkboxes](https://dwatow.github.io/JavaScript30/10%20Hold%20Shift%20and%20Check%20Checkboxes) 31 | 11. [Custom Video Player](https://dwatow.github.io/JavaScript30/11%20Custom%20Video%20Player) 32 | 12. [Key Sequence Detection](https://dwatow.github.io/JavaScript30/12%20Key%20Sequence%20Detection) 33 | 13. [Slide in on Scroll](https://dwatow.github.io/JavaScript30/13%20Slide%20in%20on%20Scroll) 34 | 14. [JavaScript References VS Copying](https://dwatow.github.io/JavaScript30/14%20JavaScript%20References%20VS%20Copying) 35 | 15. [LocalStorage](https://dwatow.github.io/JavaScript30/15%20LocalStorage) 36 | 16. [Mouse Move Shadow](https://dwatow.github.io/JavaScript30/16%20Mouse%20Move%20Shadow) 37 | 17. [Sort Without Articles](https://dwatow.github.io/JavaScript30/17%20Sort%20Without%20Articles) 38 | 18. [Adding Up Times with Reduce](https://dwatow.github.io/JavaScript30/18%20Adding%20Up%20Times%20with%20Reduce) 39 | 19. [Webcam Fun](https://dwatow.github.io/JavaScript30/19%20Webcam%20Fun) 40 | 20. [Speech Detection](https://dwatow.github.io/JavaScript30/20%20Speech%20Detection) 41 | 21. [Geolocation](https://dwatow.github.io/JavaScript30/21%20Geolocation) 42 | 22. [Follow Along Link Highlighter](https://dwatow.github.io/JavaScript30/22%20Follow%20Along%20Link%20Highlighter) 43 | 23. [Speech Synthesis](https://dwatow.github.io/JavaScript30/23%20Speech%20Synthesis) 44 | 24. [Sticky Nav](https://dwatow.github.io/JavaScript30/24%20Sticky%20Nav) 45 | 25. [Event Capture, Propagation, Bubbling and Once](https://dwatow.github.io/JavaScript30/25%20Event%20Capture%2C%20Propagation%2C%20Bubbling%20and%20Once) 46 | 26. [Stripe Follow Along Nav](https://dwatow.github.io/JavaScript30/26%20Stripe%20Follow%20Along%20Nav) 47 | 27. [Click and Drag](https://dwatow.github.io/JavaScript30/27%20Click%20and%20Drag) 48 | 28. [Video Speed Controller](https://dwatow.github.io/JavaScript30/28%20Video%20Speed%20Controller) 49 | 29. [Countdown Timer](https://dwatow.github.io/JavaScript30/29%20Countdown%20Timer) 50 | 30. [Whack A Mole](https://dwatow.github.io/JavaScript30/30%20Whack%20A%20Mole) 51 | --------------------------------------------------------------------------------