---
# System prepended metadata

title: +_JS30 1~10 不要看都在亂寫
tags: [DOM]

---

# +_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：
![image](https://hackmd.io/_uploads/r19DDA5jR.png)

```javascript!
window.addEventListener("keydown", function (keyboard) {
  console.log("按下了鍵盤上的一個鍵:", keyboard.key);
});
```
![image](https://hackmd.io/_uploads/BJxCwA5s0.png)

所以這個監聽 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 內容
![image](https://hackmd.io/_uploads/r1MWJTYo0.png)

那要我按什麼就印什麼呢？！ 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);
});
```
沒拿到東西！為什麼！
![image](https://hackmd.io/_uploads/BJj2zTYi0.png)

因為 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);
});
```

![image](https://hackmd.io/_uploads/rJp6WTYsA.png)

### 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')

![image](https://hackmd.io/_uploads/ryl9pn5sR.png)
```javascript!
key.classList.add('playing')
key.classList.remove('playing')
key.classList.toggle('playing')
// 偵測有沒有 key 這個 class ，沒有的話會加上它 / 有的話會移除
```

## document.querySelectorAll()
```javascript!
const keys = document.querySelectorAll(".key"); // 印出一個 NodeList
```
![image](https://hackmd.io/_uploads/SkkIHk12C.png)
![image](https://hackmd.io/_uploads/S1jyryJ20.png)

## transitioned 事件
[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/transitionend_event)

點選 A，結果將這些東東都印出來了
![image](https://hackmd.io/_uploads/SkraLykhA.png)

只要印 propertyName 是 transform 的（為什麼？？？？

A 的 this 是 addEventListener 的 key 
![image](https://hackmd.io/_uploads/SJeAvkJh0.png)

# 02
先看一下 html 結構，是三隻針都疊在9點:)
![image](https://hackmd.io/_uploads/r1WIB6Gn0.png)

想要讓他在12點鐘方向並且是垂直站著的。
我們先讓他 轉90度
![image](https://hackmd.io/_uploads/ryVHSpfnC.png)

咦，怎麼變成在自己這邊轉，因為！預設會是由自己的中心轉

於是我們要改變一下 transform-origin 也就是轉的原點
設置為 `transform-origin:100%`(x軸的 100%)
![image](https://hackmd.io/_uploads/HJsDBTM2A.png)
就成功拉

## Date() 
![image](https://hackmd.io/_uploads/rJcZS0Gh0.png)
## getSeconds
![image](https://hackmd.io/_uploads/Hy5LSCMnC.png)

## Date() 是一個 Class
本身是一個類別可以有 get . set 等等行為，因此可以用 Date() 來理解類別的行為，而用 valueOf() 可以拿到最接近未處理的資料
![image](https://hackmd.io/_uploads/B1X7wAM3C.png)

## 印出每秒的秒數
於是我們可以做出一個 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
![image](https://hackmd.io/_uploads/SJWdMlEn0.png)

```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 印出來
![image](https://hackmd.io/_uploads/rkjbZZVn0.png)
所以我如果另外設置其他的，data- 也是會有
```javascript!
<input
  id="blur"
  type="range"
  name="blur"
  min="0"
  max="25"
  value="10"
  data-sizing="px"
  data-mood="unhappy"
  data-interest="sleep"/>
```
![image](https://hackmd.io/_uploads/HkZwW-EhR.png)

所以可以用物件方式取得
```javascript!
console.log(this.dataset.sizing);
```
spacing 和 blur 會印出 px，color 的就會印出 undefined 
![image](https://hackmd.io/_uploads/BJ_r7WNnR.png)

所以要加一個
```javascript!
const suffix = this.dataset.sizing || "";
```
![image](https://hackmd.io/_uploads/Syie6E4nA.png)

## documentElement 
MDN：Document.documentElement 會回傳目前文件（document）中的根元素（Element），如：HTML 文件中的 <html> 元素。
![image](https://hackmd.io/_uploads/SkZyAr42A.png)

## documentElement.style
拿到元素後，直接用 .style 方式設置 inline style

其實這邊用 .body.style 也是可以，就會改在 body 上
![image](https://hackmd.io/_uploads/Sy5RWUN30.png)

## 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
```
![image](https://hackmd.io/_uploads/BJhfiS42C.png)

完成....... 好不直覺，好難！！！！但我的 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);
```
![image](https://hackmd.io/_uploads/BJrRg3hJyx.png)

## 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);
```
![image](https://hackmd.io/_uploads/SyewW3nyJl.png)

## 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);
```
![image](https://hackmd.io/_uploads/SkzMV33kyl.png)

	
## 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
![image](https://hackmd.io/_uploads/BJTOrNCkJg.png)

因此要先拿到這五個 div。
```javascript!
document.querySelectorAll(".panel")
```
![image](https://hackmd.io/_uploads/HkXxLE0yyl.png)
(用 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
![image](https://hackmd.io/_uploads/Syps54Akke.png)
所以就有做這兩件事
![image](https://hackmd.io/_uploads/ryrpYNRJkg.png)

現在不知道為什麼解答寫是在 `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) 的一個方法！
![image](https://hackmd.io/_uploads/Hk5I8I1gke.png)
`.json()` 可將格式整理為 `json`，並回傳一個 Promise
	
Promise 中我們觀察到，拿到了結果，是一個 Array
![image](https://hackmd.io/_uploads/S1838U1xJx.png)

為什麼叫 blob？
感覺是一個 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Blob) 說的：
> Blob 对象表示一个不可变、原始数据的类文件对象

而剛剛拿到的是 Promise 的結果，我們要取得來做事情，必須要再 .then，所以我們把它 .then 然後 log 出來，就真的拿到資料惹！
![image](https://hackmd.io/_uploads/By72uI1lyg.png)

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

![image](https://hackmd.io/_uploads/r1DhlDJlkl.png)

成功接收到。

## 展開運算子對付二維陣列
但目前 cities 它是一個 nest 的陣列，它有一個裡面是 1000 個元素的元素。

可以在要 push 進去的時候展開他。
等於你會把要 push 進去的陣列展開，例如 [1,2,3,4,5] 展開變 1,2,3,4,5 裝進去，那就會在第一層。

![image](https://hackmd.io/_uploads/Hkg3VwJe1l.png)

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"

就會回傳匹配的
![image](https://hackmd.io/_uploads/SylTpKVxyg.png)

## [正則使用](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

輸入框的
![image](https://hackmd.io/_uploads/HJW4N9Ng1l.png)

顯示的白色區塊
![image](https://hackmd.io/_uploads/HyRwN9Egyx.png)

接下來我們監聽 input 的 change 事件，然後印出當下輸入的值看看
	
```javascript!
function displayMatches() {
  console.log(this.value);
}

const searchInput = document.querySelector(".search");
const suggestion = document.querySelector(".suggestion");

searchInput.addEventListener("change", displayMatches);
```

![image](https://hackmd.io/_uploads/ry0485VeJe.png)

沒問題，所以我們可以用這個 function 去顯示匹配的結果然後顯示 ui

```javascript!
function displayMatches() {
  const matchArray = findMatches(this.value, cities);
  console.log(matchArray);
}
```
![image](https://hackmd.io/_uploads/S1l8_c4lyg.png)

## 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 事件印出來會是
![image](https://hackmd.io/_uploads/HyfYZvPxkx.png)

因此我們可以知道，這就是滑鼠位置的座標，offsetX,offsetY

我們可以拿來作為 lineTo
也就是我們滑鼠到哪裡，就畫到哪裡

但由於 lastX, lastY 都是 0，所以會變成這樣
![image](https://hackmd.io/_uploads/Bym3XHigyg.png)

從 0 畫到 滑鼠 mousemove 的位置

因此我們的 moveTo 初始地點，需要改一下

應該設置為 `lastX = e.offsetX;
lastY = e.offsetY;`

但現在因為初始值依舊是 0，所以我們等於從 0 開始畫，然後後來接著 mousemove 的地方 event 的 offsetX,offsetY 畫，變成醬子
![image](https://hackmd.io/_uploads/Syg94Hsxkl.png)

這時候我們應該想著我們畫畫的行為
mousedown 會在 mousemove 之前發生，因此我們要在 mousedown 前就設置 `lastX = e.offsetX;
lastY = e.offsetY;`！
![image](https://hackmd.io/_uploads/S1jatrixJg.png)

就可以自由的畫畫啦

接下來要設置畫筆顏色和粗細
畫筆顏色：
[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 ++;
```
![image](https://hackmd.io/_uploads/H1FhSUieye.png)

因此就會有顏色變化

畫筆粗細：
`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','🙁')
```
![image](https://hackmd.io/_uploads/SkjKyvolJg.png)

## css 用法 %c
![image](https://hackmd.io/_uploads/BkFMlwsl1x.png)
![image](https://hackmd.io/_uploads/HkQVeDox1e.png)
![image](https://hackmd.io/_uploads/BylPeDsgJg.png)
	
跟字有關的就好，隨你玩

## warn
![image](https://hackmd.io/_uploads/B1TslPjx1g.png)

## error
![image](https://hackmd.io/_uploads/S1EAePjxkx.png)

## info
![image](https://hackmd.io/_uploads/rJGNbDigkl.png)

## assert 
![image](https://hackmd.io/_uploads/SyKqbDilke.png)

前面這段是錯的才會顯示

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

![image](https://hackmd.io/_uploads/B1cSUwslyg.png)

## clear
```javascript!
console.clear()
```
![image](https://hackmd.io/_uploads/rJ5udvse1e.png)

就是 clear

## dir
可以看到整個 DOM 元素的東東
![image](https://hackmd.io/_uploads/Skk1tPslJe.png)

## 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}`);
});
```
	
![image](https://hackmd.io/_uploads/HJXWsDslyx.png)

也可以設置 groupCollapsed
`console.groupCollapsed(`${dog.name}`);`

一開始預設就會是關著的 
![image](https://hackmd.io/_uploads/rJiCjDogyx.png)

## count
可以算出在 count 中用了幾次
![image](https://hackmd.io/_uploads/r18i3vsg1e.png)

## 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);
  });
```	

![image](https://hackmd.io/_uploads/BJURgOjekl.png)

# 10
	
click 事件有 .shiftKey 可以看到是否是按著 shift 點擊的

這一題有個範圍因此可以先設置標記最後點擊的，最後點擊的就是在 shit 前點擊的。

所以監聽點擊事件後先將 this 設置為 lastChecked

再判斷如果是 shiftKey 的話，做什麼事情，這邊因為 shiftKey 按著取消 checkbox 也會觸發所以應該要是確認是在按下 shift 並且 checkbox 被勾選的時候觸發
