---
# System prepended metadata

title: DOM相關

---

# DOM相關
DOM：文件物件模式

### querySelector
querySelector()：只會return第一個滿足css selector的html element

eg.
```
let secondH1 = document.querySelector(".second");
```

### querySelectorAll
querySelectorAll()：

eg. HTML的code可能為
```
<h1 class = "second">
<p class = "second">
<p class = "second">
```
裡面有很多element的class都是second，若要全部取出，則需要用querySelectorAll()來幫忙：
```
let seconds = document.querySelectorAll(".second");

console.log(seconds);
```
若僅需要特定的second，可以透過index number來決定要抓取哪個second。
eg. 抓取第二個second
```
console.log(seconds[1]);
```
看起來很像array，但是並非array！而是NodeList！！！

### Arrow Function Expression
* Arrow Function Expression是用來定義（製作）function的方法。
* 跟function Declaration不同，但運作十分類似。
* 不同點在於：
    1. 語法不同
    2. 關鍵字「this」

先來比較語法：
```
// function declaration
function sayHi() {
    console.log("Hi");
}

// arrow function expression
let sayHi = () => {
    colsole.log("Hi");
};

```
let sayHi = ()：告知JavaScript sayHi是一個方法；後面箭頭（=>）要接大括號{};
當然也可以在小括號內寫一些value：
```
let sayHi = (name) => {
    colsole.log("Hi" + name);
};

sayHi("Jimmy");
```
一般而言，JavaScript的特性是「由上往下執行」，也就是先跑的要先寫；function declaration的特性是允許先執行方法（寫在上面），再把方法宣告出來（寫在下面），像是這樣：
```
sayHi();  <-先執行方法

function sayHi() {        <-再宣告方法
    console.log("Hi");
}
```
當然反過來寫，先宣告再執行也OK。
但在Arrow Function Expression中無法使用這招，會出現「Uncaught ReferenceError」，要求要先初始化（initialize，意即creat an object）才能使用。
簡言之就是，在我們定義sayHi這個function以前，我們不能去使用他。所以程式碼只能這樣處理：
```
let sayHi = () => {
    console.log("Hi");
};

sayHi();
```
#### this keyword：念書要看上下文
在function declaration中，this一般而言是指他所在的object；但在arrow function expression中，this會指向「他上一層的thi̍s」；若他上一層沒東西，則會指向window object，如下所示：
```
let Wilson = {
    name: "Wilson Ren",
    //function declaration
    greeting() {
        console.log(this);    <-這裡的this指Wilson這個object
        console.log("Hi, my name is " + this.name + ".");
    },
    
    //arrow function expression
    walk: () => {
        console.log(this);    <-這裡的this指Window object
        console.log(this.name + " is walking on the street.");
    }
}

Wilson.greeting();
Wilson.walk();
```
this指向上層的this的範例：
```
function Person(){             <-我就是傳說中的上一層
    this.age = 0;

    setInterval(() => {
        this.age++;            <-this去抓了上層Person建構式所建立的物件
    }, 1000);
}

var p = new Person();
```

##### 小小結：
* arrow function expression沒有屬於自己的this變數，在使用this的時候，會看自己所在的位置的上一層function有沒有其他this可以對照；若無，則最終指向window function（感覺window function跟Java「大家的老爸」object父類別有點像！？）
* 一般而言，在object裡面不太會使用arrow function expression，但寫DOM時會大量使用。
* arrow function expression在使用前需要先給一個變數，例如：
```
let sayHi = () => {}
```
參考資料：（中文的喔~）
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions


### forEach Function
另一種跑迴圈的方式。（Another way to loop through an array.）
forEach function會以其設定的條件，對陣列中每個元素都跑一次，例如下列程式：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];
luckyNumbers.forEach(function checkNum(n) {
    if (n > 20) {
        console.log(n);
    }
});
```
這意味著forEach function從第一個元素比對到最後一個元素，並透過console.log會把陣列內>20的元素列印出來。
其實整個程式碼與下面「for loop」的程式碼是一樣的：
```
for (let i = 0; i < luckyNumbers.length; i++) {
    if (luckyNumbers[i] > 20) {
        console.log(luckyNumbers[i]);
    }
}
```
forEach function也可以這樣寫：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];
luckyNumbers.forEach(checkNum());

function checkNum(n) {
    if (n > 20) {
        console.log(n);
    }
}
```
欸那個，checkNum那麼多，好像可以合併在一起吼：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];

luckyNumbers.forEach(function checkNum(n) {
    if (n > 20) {
        console.log(n);
    }
});
```
一般而言，我們幫function取名字的目的是為了要在之後執行他，但forEach function卻是「當下」就自動執行，在當下就執行完畢的情況下，幫function取名這個行為就變得十分雞肋。故checkNum這個名字可以直接刪掉，讓forEach function沒有名字的情況下仍然可以執行，也就是所謂的「匿名函式」（anonymous function）。
用匿名函式的寫法，可以把程式碼再簡化：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];

luckyNumbers.forEach(function(n) {
    if (n > 20) {
        console.log(n);
    }
});
```
匿名函式算是一種function declaration。
既然function declaration都出現了，那他的宿敵----arror function expression當然也要參戰囉！
首先我們把程式碼刪光光，僅留下這樣：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];

luckyNumbers.forEach();
```
在forEach()中間塞一個小括號，小括號內寫上n，並在小括號後面畫個箭頭：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];

luckyNumbers.forEach((n) =>);
```
在箭頭後面填上大括號，在大括號內部寫判斷式：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];

luckyNumbers.forEach((n) => {
    if (n > 20) {
        console.log(n);
    }
});
```
以上就是在forEach function裡加入arror function expression的方式。
另外更誇張的是，其實forEach((n))，那個包在n外面的小括號也可以不用寫......

### CallBack function
延續剛剛forEach function與其程式碼：
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];

luckyNumbers.forEach(
    n => {               <-從n開始 
    if (n > 20) {        <-這一整段
        console.log(n);  <-就是
    }                    <-所謂的
    }                    <-CallBack function
);
```
CallBack有「打回來」的意思，在forEach function中顧名思義，每執行一次就會執行CallBack function，打回來再執行下一個元素。
CallBack function可執行的有：
* currenValue
* index（可加可不加）
* array（可加可不加）
* thisArg（可加可不加）
```
let luckyNumbers = [7, 15, 23, 66, 91, 10, 13];

luckyNumbers.forEach((n, index, arr) => {             
    if (n > 20) {        
        console.log(n);
        console.lon(n + " is at index number " + index);
        console.log(arr);
    }                    
});
```