# +_JS30 1~10 不要看都在亂寫
[TOC]
# 01
## addEventListener
> window.addEventListener() 是一個監聽事件的 API,當事件觸發時,會執行指定的 callback function。
其中的參數會是:("此監聽事件的名稱", 此事件要做的 callback function)
## keydown 事件
> 當按下鍵盤上的任意鍵時,這段程式碼會被執行,而 keyboard 是"點選了哪個鍵"的該物件
```javascript!
window.addEventListener("keydown", function (keyboard) {
console.log(keyboard);
});
```
例如我點了 enter 鍵,印出來就會是一個帶有 key: "Enter" 這個資訊的 KeyboardEvent:

```javascript!
window.addEventListener("keydown", function (keyboard) {
console.log("按下了鍵盤上的一個鍵:", keyboard.key);
});
```

所以這個監聽 API 執行了 keydown 事件,幫我監聽了我點選哪個按鍵,然後我可以在 callback function 看我要幹啥!(你想幹啥就幹啥)
## document.querySelector
> querySelector 方法用來選取符合指定 CSS 選擇器的第一個 DOM 元素。可以選擇特定的標籤、類名、ID 或屬性。
我的 html 有一個 audio tag
```html!
<audio data-key="65" src="sounds/clap.wav"></audio>
```
```javascript!
window.addEventListener("keydown", function (keyboard) {
const audio = document.querySelector('audio[data-key="65"]');
console.log(audio);
});
// 每次監聽到我按下按鍵,都會抓到 audio 這個 tag 的[data-key="65"] 這個屬性,然後印出來
```
印出來會是啥!瘋狂按每個按鍵就是印出這個 65 的 tag 內容

那要我按什麼就印什麼呢?! babe what should I do
讓我們將 `document.querySelector('audio[data-key=""]')` "" 中的參數改為 keyboard 變數試試,這個 keyboard 就是監聽到的物件
```javascript!
window.addEventListener("keydown", function (keyboard) {
const audio = document.querySelector(`audio[data-key="${keyboard}"]`);
console.log(audio);
});
```
沒拿到東西!為什麼!

因為 keyboard 是一个 KeyboardEvent 物件,而不是一个可以直接用於 data-key 屬性的值。
於是我們要指定印出物件其中的值,才可以啦,例如 keyCode
```javascript!
window.addEventListener("keydown", function (keyboard) {
const audio = document.querySelector(`audio[data-key="${keyboard.keyCode}"]`);
// 選取所有 <audio> 標籤,並且 data-key 屬性值等於當前按鍵的 keyCode 的元素
console.log(audio);
});
```

### GPT 解釋一下啊
```javascript!
window.addEventListener:這是一個 API 用來監聽指定的事件。
事件參數:keyboard 是事件回調函數的參數,當事件發生時,它會傳遞一個事件對象(在這裡是 KeyboardEvent)。這個對象包含有關事件的詳細信息,例如被按下的鍵的 keyCode、key、altKey 等屬性。
document.querySelector:這是一個 DOM 方法,用來選取符合指定 CSS 選擇器的第一個元素。在這個例子中,audio[data-key="${keyboard.keyCode}"] 的意思是選取所有 <audio> 標籤,並且 data-key 屬性值等於當前按鍵的 keyCode 的元素。
結合:當用戶按下鍵盤時,keydown 事件會觸發,你的回調函數會接收到一個 KeyboardEvent 對象,然後通過 keyboard.keyCode 獲取按下的鍵的 keyCode。接著你使用這個 keyCode 來選取對應的 <audio> 元素。
```
## audio.play()
這是 for `<audio>` tag 用的,播放功能
## audio.currentTime
[MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/currentTime)
設置歸零的話會是有一個 reset 的功用:將播放時間設置為 0,即重新從頭播放
## classList.add / classList.remove / classList.toggle
> 要操作在哪個class名稱上.classList.動作('要加上的 class')

```javascript!
key.classList.add('playing')
key.classList.remove('playing')
key.classList.toggle('playing')
// 偵測有沒有 key 這個 class ,沒有的話會加上它 / 有的話會移除
```
## document.querySelectorAll()
```javascript!
const keys = document.querySelectorAll(".key"); // 印出一個 NodeList
```


## transitioned 事件
[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/transitionend_event)
點選 A,結果將這些東東都印出來了

只要印 propertyName 是 transform 的(為什麼????
A 的 this 是 addEventListener 的 key

# 02
先看一下 html 結構,是三隻針都疊在9點:)

想要讓他在12點鐘方向並且是垂直站著的。
我們先讓他 轉90度

咦,怎麼變成在自己這邊轉,因為!預設會是由自己的中心轉
於是我們要改變一下 transform-origin 也就是轉的原點
設置為 `transform-origin:100%`(x軸的 100%)

就成功拉
## Date()

## getSeconds

## Date() 是一個 Class
本身是一個類別可以有 get . set 等等行為,因此可以用 Date() 來理解類別的行為,而用 valueOf() 可以拿到最接近未處理的資料

## 印出每秒的秒數
於是我們可以做出一個 function 去模擬印出每秒的秒數
```javascript!
function setTime() {
const now = new Date();
const seconds = now.getSeconds();
console.log(seconds);
}
setInterval(setTime, 1000);
```
### 換成度數到秒針上
```javascript!
const secondHand = document.querySelector("second-hand");
function setTime() {
const now = new Date();
const seconds = now.getSeconds();
const secondsDegrees = (seconds / 60) * 360;
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
}
```
# Playing with CSS Variables and JS
先將要改變型態的東東設上 css
```javascript!
:root {
--base: #f3c944;
--spacing: 10px;
--blur: 10px;
}
img {
padding: var(--spacing);
background: var(--base);
filter: blur(var(--blur));
}
.hl {
color: var(--base);
}
```
先選到這三個 input

```javascript!
const input = document.querySelectorAll('.controls input')
```
設定事件
```javascript!
const input = document.querySelectorAll(".controls input");
function handleUpdate() {
console.log(this.value);
}
// 每個 input 都監聽 change 事件,並且執行 handleUpdate function
input.forEach((input) => input.addEventListener("change", handleUpdate));
input.forEach((input) =>
input.addEventListener("mousemove", handleUpdate)
);
```
## change 事件
[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/change_event)
## mousemove 事件
[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/mousemove_event)
## data
data 是一個物件 - 後面是他的屬性,我有一個 input 元素有 data-sizing 的屬性
```javascript!
<input
id="blur"
type="range"
name="blur"
min="0"
max="25"
value="10"
data-sizing="px"/>
<script>
const input = document.querySelectorAll(".controls input");
function handleUpdate() {
console.log(this.dataset);
}
input.forEach((input) => input.addEventListener("change", handleUpdate));
input.forEach((input) =>
input.addEventListener("mousemove", handleUpdate)
);
// 為什麼要加 mousemove 只是因為要在拖曳的時候要隨時抓到值
</script>
```
我可以用 dataset 這個屬性把他的 value 印出來

所以我如果另外設置其他的,data- 也是會有
```javascript!
<input
id="blur"
type="range"
name="blur"
min="0"
max="25"
value="10"
data-sizing="px"
data-mood="unhappy"
data-interest="sleep"/>
```

所以可以用物件方式取得
```javascript!
console.log(this.dataset.sizing);
```
spacing 和 blur 會印出 px,color 的就會印出 undefined

所以要加一個
```javascript!
const suffix = this.dataset.sizing || "";
```

## documentElement
MDN:Document.documentElement 會回傳目前文件(document)中的根元素(Element),如:HTML 文件中的 <html> 元素。

## documentElement.style
拿到元素後,直接用 .style 方式設置 inline style
其實這邊用 .body.style 也是可以,就會改在 body 上

## setProperty()
.style 要寫 inline style 後,.setProperty 就是可以指定屬性名稱並寫入值!
```javascript!
element.style.setProperty(propertyName, value, priority);
// propertyName, value 都用字串形式
```
```javascript!
document.documentElement.style.setProperty(
`--${this.name}`,
`${this.value}${suffix}`
// 後綴記得要加上
);
// 所以說監聽到哪個 element 就改他的 name, 也改成他的 value
```

完成....... 好不直覺,好難!!!!但我的 Jerry 很可愛
> 可愛鼠了 [name=橘子]
# 04
## Array.prototype.filter()
1. Filter the list of inventors for those who were born in the 1500's
```javascript!
const ageFilter = inventors.filter((inventor) => {
return inventor.year < 1800;
});
可以簡寫為
const ageFilter = inventors.filter((inventor) => inventor.year < 1800);
console.table(ageFilter);
```

## Array.prototype.map()
2. Give us an array of the inventors first and last names
```javascript!
const fullName = inventors.map(
(inventor) => inventor.first + " " + inventor.last
);
console.table(fullName);
```

## Array.prototype.sort()
3. Sort the inventors by birthdate, oldest to youngest
```javascript!
const sortBirth = inventors.sort(function (inventorA, inventorB) {
if (inventorA.year > inventorB.year) {
return 1;
} else {
return -1;
}
});
const sortBirth = inventors.sort((inventorA, inventorB) =>
inventorA.year > inventorB.year ? 1 : -1
);
console.table(sortBirth);
```

## 6. create a list of Boulevards in Paris that contain 'de' anywhere in the name
https://en.wikipedia.org/wiki/Category:Boulevards_in_Paris
```javascript!
const category = document.querySelector(".mw-category");
const links = Array.from(category.querySelectorAll("a"));
const de = links
.map((link) => link.textContent)
.filter((streetName) => streetName.includes("de"));
```
```javascript!
document.querySelector(".mw-category");
```
return 的是一個 NodeList,可以用 `Array.from` 或 `[...]` 展開成一個陣列。
## 8. Reduce Exercise
Sum up the instances of each of these
```javascript!
const data = [
"car",
"car",
"truck",
"truck",
"bike",
"walk",
"car",
"van",
"bike",
"walk",
"car",
"van",
"car",
"truck",
];
```
關鍵:用 reduce 初始化值設置為一個物件 `{}`,數到後、就加一次 `obj[item] = 0;` 但這個是為了在第一次的時候初始值。
例如 `obj[car]` 第一次應該會是 NaN。
```javascript!
const countDataTimes = data.reduce((obj, item) => {
if (!obj[item]) {
obj[item] = 0;
}
obj[item]++;
return obj;
}, {});
console.log(countDataTimes);
```
# 05
script 部分:
先想好要拿到誰,目前想要操作的是這五個 div,監聽點擊事件、之後對其加上 class

因此要先拿到這五個 div。
```javascript!
document.querySelectorAll(".panel")
```

(用 querySelector 只會拿到第一個)
接下來要對每一個做什麼呢?
1. 用 forEach 遍歷其中的元素
2. 監聽 click 事件、觸發 function 讓他加上 open 這個 class
```javascript!
function toggleOpen() {
this.classList.toggle("open");
}
panels.forEach((panel) => {
panel.addEventListener("click", toggleOpen);
});
```
可以用 `classList.toggle()` 動態加上 class
```javascript!
classList.toggle("要加上的 class 名稱")
```
接下來因為要在整個展開的時候,將文字放進來。
可以先看 panel 整個展開之後會做什麼。
```javascript!
function toggleActive(e) {
console.log(e);
}
panels.forEach((panel) => {
panel.addEventListener("transitionend", toggleActive);
});
```
因為加上是 open 這個 class

所以就有做這兩件事

現在不知道為什麼解答寫是在 `propertyName.includes("flex")` 的時候,要加上 .opne-active 而不是 `font` 的時候,應該是因為想要讓他們同時字變大!??
或可能都可以?:)
現在我們要做的是:
`function toggleActive() {}` 在其中把 open.active class 這個加上
```javascript!
function toggleActive(e) {
if (e.propertyName.includes("font")) {
this.classList.toggle("open-active");
}
}
```
open.active 要注意加上之後是要指定其中的第一個和第三個 p 做動作。
```javascript!
.panel.open-active p:nth-child(1) {
transform: translateY(0);
font-size: 2rem;
}
.panel.open-active p:nth-child(3) {
transform: translateX(0);
font-size: 2rem;
}
```
# 06 - Type Ahead
1. 取得 api 的資料,整理格式
2. 監聽 input 的輸入
3. 取得輸入的資料,並與所有資料比對,過濾出想要拿到的資料
4. 將想要拿到的資料放到介面上,對介面的東西做視覺(黃色標示、格式化)
將資料轉化成一段 html code 然後再把監聽的 DOM 元素的 innerHTML 改為這段
## fetch
fetch 是一個 api,會回傳 Promise
fetch 之後,我們取得一個 Promise,而在 Prototype 裏,看到 [json](https://developer.mozilla.org/zh-CN/docs/Web/API/Response/json) 的一個方法!

`.json()` 可將格式整理為 `json`,並回傳一個 Promise
Promise 中我們觀察到,拿到了結果,是一個 Array

為什麼叫 blob?
感覺是一個 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob) 說的:
> Blob 对象表示一个不可变、原始数据的类文件对象
而剛剛拿到的是 Promise 的結果,我們要取得來做事情,必須要再 .then,所以我們把它 .then 然後 log 出來,就真的拿到資料惹!

接下來我們要把它存到一個地方做事,首先先定義一個 cities 的 array,然後再 push 進去:

成功接收到。
## 展開運算子對付二維陣列
但目前 cities 它是一個 nest 的陣列,它有一個裡面是 1000 個元素的元素。
可以在要 push 進去的時候展開他。
等於你會把要 push 進去的陣列展開,例如 [1,2,3,4,5] 展開變 1,2,3,4,5 裝進去,那就會在第一層。

cities 就變成貨真價實的 1000 個元素了。
接下來要製作一個 function 是可以偵測到你輸入的字,以及去查找 cities 裡面匹配的東西。
## 字串匹配正則
[String.prototype.match()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/match)
match 裡面可以放 `/字串/i` 這樣就會完全匹配裡面的字,例如我寫 `/Bos/i`,function 丟 "Bos"
就會回傳匹配的

## [正則使用](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
```javascript!
new RegExp(這裡面放你寫正則的驗證)
```
EX
```javascript!
new RegExp(wordToMatch, "gi");
```
gi:The gi modifier is used to do a case-insensitive search of all occurrences of a regular expression in a string.
不區分大小寫的!
接下來要做的是當我們在 input 輸入東西的時候,要有介面的變化
要怎麼做?
監聽 input 拿到值,之後顯示匹配的東西,並且更新 ui。
監聽和顯示 都要用到 DOM
所以可以先拿這兩個地方的 DOM
輸入框的

顯示的白色區塊

接下來我們監聽 input 的 change 事件,然後印出當下輸入的值看看
```javascript!
function displayMatches() {
console.log(this.value);
}
const searchInput = document.querySelector(".search");
const suggestion = document.querySelector(".suggestion");
searchInput.addEventListener("change", displayMatches);
```

沒問題,所以我們可以用這個 function 去顯示匹配的結果然後顯示 ui
```javascript!
function displayMatches() {
const matchArray = findMatches(this.value, cities);
console.log(matchArray);
}
```

## replace 的方法
[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
步驟寫在 code 註解....
# 08
學 Canvas
[第十七章、你好啊鐵人們,接下來就由我鎖鏈 Canvas 對付你!(壹)](https://ithelp.ithome.com.tw/articles/10248443)
## [getContent()](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/getContext)
```javascript!
canvas.getContext("2d")
// "2d", 建立一个 CanvasRenderingContext2D 二维渲染上下文。這邊就是可以畫畫的地方。
```
## [.strokeStyle](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors)
[CanvasRenderingContext2D:strokeStyle 属性](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/strokeStyle)
畫筆的顏色?
## [.lineJoin](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineJoin)
畫筆的樣式?
## [.lineCap](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineJoin)
畫筆末端的樣式?
設置一個 flag 控制:滑鼠點擊的時候可畫畫,放開時就不能畫畫。
所以我們先將一個變數設置為 false,然後監聽滑鼠的 mousemove 事件,當事件被觸發,
## 畫畫套組 [beginPath. moveTo . lineTo. stroke](https://www.w3schools.com/jsref/tryit.asp?filename=tryhtml5_canvas_moveto)
stroke 是畫實線
## [fillRect](https://www.w3schools.com/jsref/tryit.asp?filename=tryhtml5_canvas_fillrect)
## [strokeRect](https://www.w3schools.com/jsref/tryit.asp?filename=tryhtml5_canvas_strokerect)
mousemove 事件印出來會是

因此我們可以知道,這就是滑鼠位置的座標,offsetX,offsetY
我們可以拿來作為 lineTo
也就是我們滑鼠到哪裡,就畫到哪裡
但由於 lastX, lastY 都是 0,所以會變成這樣

從 0 畫到 滑鼠 mousemove 的位置
因此我們的 moveTo 初始地點,需要改一下
應該設置為 `lastX = e.offsetX;
lastY = e.offsetY;`
但現在因為初始值依舊是 0,所以我們等於從 0 開始畫,然後後來接著 mousemove 的地方 event 的 offsetX,offsetY 畫,變成醬子

這時候我們應該想著我們畫畫的行為
mousedown 會在 mousemove 之前發生,因此我們要在 mousedown 前就設置 `lastX = e.offsetX;
lastY = e.offsetY;`!

就可以自由的畫畫啦
接下來要設置畫筆顏色和粗細
畫筆顏色:
[hsl](https://developer.mozilla.org/zh-CN/docs/Web/CSS/color_value/hsl)(0,100,50);
0 是紅色,100 飽和度 100,50 代表正常
```javascript!
ctx.strokeStyle = `hsl(0, 100%, 50%)`;
```
接下來我們因為要變顏色,所以我們可以在第一個參數設置成變數(這邊設置為 hue)
```javascript!
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
```
以及我們在最後設置
```javascript!
hue ++;
```

因此就會有顏色變化
畫筆粗細:
`lineWidth`
畫筆粗細變化:
這邊注意是到一個極限值就又變小
等於會是:細變粗,粗變細
這邊做法是先設置一個 direction = true 代表要控制畫筆粗細的條件
所以我們先讓畫筆變粗
```javascript!
// 先讓畫筆變粗,讓他加到 100,加到 100 之後會進 else 開始變小,變小到 1 之後又會變相反,又進 if 開始加
if (ctx.lineWidth === 100 || ctx.lineWidth === 1) {
direction = !direction;
}
if (direction) {
ctx.lineWidth++;
} else {
ctx.lineWidth--;
}
```
# 09
console.log 的怪怪用法
## 插值用法 %s
```javascript!
console.log('Hello I am a %s string','🙁')
```

## css 用法 %c



跟字有關的就好,隨你玩
## warn

## error

## info

## assert

前面這段是錯的才會顯示
```javascript!
console.assert(p.classList.contains('ouch'),'it is wrong!')
```
這會檢查 p 有沒有 'ouch' 這個 class 沒有就會顯示

## clear
```javascript!
console.clear()
```

就是 clear
## dir
可以看到整個 DOM 元素的東東

## group
可以設置 group 和 groupEnd
```javascript!
dogs.forEach((dog) => {
console.log(`${dog.name}`);
console.log(`${dog.name} is ${dog.age} years old`);
console.log(`${dog.name} is ${++dog.age} years old next year`);
});
dogs.forEach((dog) => {
console.group(`${dog.name}`);
console.log(`${dog.name}`);
console.log(`${dog.name} is ${dog.age} years old`);
console.log(`${dog.name} is ${++dog.age} years old next year`);
console.groupEnd(`${dog.name}`);
});
```

也可以設置 groupCollapsed
`console.groupCollapsed(`${dog.name}`);`
一開始預設就會是關著的

## count
可以算出在 count 中用了幾次

## time
可以算時間
`console.time(設置要算時間的東西)`
```javascript!
console.time("fetch endpoint");
fetch("https://api.github.com/users/wesbos")
.then((data) => data.json())
.then((data) => {
console.timeEnd("fetch endpoint");
console.log(data);
});
```

# 10
click 事件有 .shiftKey 可以看到是否是按著 shift 點擊的
這一題有個範圍因此可以先設置標記最後點擊的,最後點擊的就是在 shit 前點擊的。
所以監聽點擊事件後先將 this 設置為 lastChecked
再判斷如果是 shiftKey 的話,做什麼事情,這邊因為 shiftKey 按著取消 checkbox 也會觸發所以應該要是確認是在按下 shift 並且 checkbox 被勾選的時候觸發