---
# System prepended metadata

title: '[FE102] 前端必備：JavaScript'
tags: [Frontend]

---

# [FE102] 前端必備：JavaScript
###### tags:`Frontend`
[TOC]

介面/事件/資料
Vanilla Js = 原生JS

## JavaScript 與瀏覽器的溝通

發送 request 然後拿到 response，這件事情都可以被 Nodejs 和 瀏覽器來達成，但是後者因為安全性的關係，會有一些限制。

### 執行 JavaScript 的一百種方式

一般來說，`<script>`會放在 HTML 的最後面，等 DOM 解析完之後再執行。或是放在最上面，然後加上`document.addEventListener('DOMContentLoaded', cb)`

```htmlembedded=
<script>
  code
</script>
```
```htmlembedded=
<script src="./index.js"></script>
```
nodejs 跟瀏覽器都只是 JS 的執行環境，兩篇支援的項目(語法)不一樣

### DOM

==把 HTML 的所有內容轉換成另一種結構==，這個結構是==樹狀結構==，JS 會對 DOM 作修改，讓瀏覽器 render 結果被改變
![一棵樹](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/DOM-model.svg/1200px-DOM-model.svg.png)

###  如何選到想要的元素：getElement

[釐清一些學習上的細節 /week5](/Ioq7YzVmQW6KgtlfLAAlNw)

```javascript=
const element = document.getElementsByTagName('div')
console.log(element)

const element = document.getElementsByClassName('card')
console.log(element)

const element = document.getElementById('card')
console.log(element)

const element = document.querySelector('#card h1')
// 對 CSS 作選擇，只會回傳被批配到的第一個元素
console.log(element)

const element = document.querySelectorAll('.card')
//回傳的是一個類似陣列的東西, 可以當成陣列來用
console.log(element)
```
###  改變元素的 CSS

用 `.style` 來作 CSS 樣式的修改，如果遇到 `padding-top` 之類的屬性，就用駝峰命名或是放在 `[ ]` 裡面，但這些方法不常用就是。 
```javascript=
const element = document.querySelector('#card h1')
element.style.paddingTop = '10px'
element.style['background-color'] = 'black'
```

### 改變元素的 Class

先把要新增的 class 寫在 CSS，再用 JS 把那個 class 新增到 HTML
```javascript=
const element = document.querySelector(".card")
console.log(element)
element.classList.add('color')
//放要新增的 class "名稱", 不用用".class", 然後 .classList.add() 到 HTML 標籤裡面
// 但這只能對第一個標籤做新增, 多個標籤的方法待查
```

```javascript=
const element = document.querySelector(".container")
console.log(element)
element.classList.remove('container')
//只對單一個標籤有效, 有子標籤的話對子標籤無效
```

```javascript=
const element = document.querySelector(".container")
console.log(element)
element.classList.toggle('color')
// toggle 會判斷有沒有這個 class, 有就把它關掉, 沒有就把它加上去
```

### 改變內容：inner、outer 的 HTML 與 text

[釐清一些學習上的細節 /week5](/Ioq7YzVmQW6KgtlfLAAlNw)
```javascript=
const element = document.querySelector("h1")
console.log('innerHTML is : \n', element.innerHTML)
console.log('outerHTML is : \n', element.outerHTML)
console.log('innerText is : \n', element.innerText)
```

* `.innerHTML`、`.outerHTML` 會連著 tag 一起選取到，前者不會把目標自身的 tag 選起來，後者會把目標自身的 tag 選起來
* `.innerText` 則是只選取 tag 裡面的文字內容，加進去 tag 裡面的也只是純文字

### 新增、插入與刪除元素：createElement, appendChild 與 removeChild

[釐清一些學習上的細節 /week5複習周](/Ioq7YzVmQW6KgtlfLAAlNw)

`appendChild` 與 `removeChild`都是從 parent 出發，也就是他們只能對子元素下手
```javascript=
const element = document.querySelector("h1")
const rm = document.querySelector(".mark")
element.removeChild(rm)
```

```javascript=
const element = document.querySelector("h1")
const add = document.createElement("p")
p.innerText = 'say hi'
element.appendChild(add)
```

```javascript=
const element = document.querySelector("h1")
const add = document.createTextNode("say hi~")
element.appendChild(add)
```

> 這些都算是 document 提供的API? yes

## JavaScript 網頁事件處理

### eventListener 與 callback function

`element.addEventListener('想監聽的事件類型', callbackFunction)`

```javascript=
const element = document.querySelector("h1")
element.addEventListener('click', onClick)
function onClick () {
  alert('say hi~')
}
```

補充一下 callbackFunction:
```javascript=
function doSomething(x, cb) {
   console.log(x)
   cb()
}

doSomething('123', () => {
   console.log('444')
})
```
以剛剛上面這個案例，doSomething 不是 callback function ，但 cb 是。

###  event(e) 是什麼？

瀏覽器會把事件的相關資料傳進去給變數 e，e 類似(?)一個 obj。
```javascript=
const element = document.querySelector("h1")
element.addEventListener('click', onClick)
function onClick (e) {
  console.log(e)
}
```

### 表單事件處理 Submit

`submit`: 送出表單的事件
`.value`: `<input>` 標籤/輸入框送出的 value

```javascript=
e.prenentDefault() //阻止瀏覽器預設的行為
```

### 比複雜更複雜的事件傳遞機制

筆記獨立寫在這:
[DOM 的事件傳遞機制：捕獲與冒泡](/4s7t2ClaTgCBlNseZoze3A)

寫一個 fn 來把事件監聽放到不同 element 裡面
```javascript=
addEvent('.outer')
addEvent('.inner')
addEvent('div')

function addEvent (className) {
  document.querySelector(className)
    .addEventListener('click', function () {
      alert('say hi')
    })
}
```

###  新手 100% 會搞錯的事件機制問題

> 一般會用 `data-XXX="1"` 來放 JS 裡面自訂的屬性
* 如果變數 i 用 var 宣告
完成 for 迴圈裡面的事情之後， i 的值已經變成 5 了，所以所有的 alert(i+1) 都變成 6。
所以可以用`data-value="1"`來作改變，或是改用 let 來處理↓

* 如果變數 i 用 let 宣告
就會正常用12345顯示

> 用`.getAttribute('')`, `.setAttribute('')` 來對 HTML標籤加上屬性
> 用`.contains`來確定某一個屬性?在不再裡面

## 如何在瀏覽器上儲存資料

> 假設我在一家線上購物網站做大採購，這個採購是屬於限量拍賣，我想先依次列完購買清單，等到限量開賣的時候再給他一次下標，該怎麼做到? (AKA 今天加入購物車的商品，我明天也要看到它，甚至在不同電腦裡面也要看到)

### Cookie

> 一個小型文字檔，會自動帶入瀏覽器
```
response

Set-Cookie

request Header

Cookie
```

### 最推薦的儲存方式：local storage

> `window.localStorage.getItem()`、`window.localStorage.setItem`

跟 Cookie 一樣是存在 client 端的資料，資料的存放方式類似於 key/value pair，這兩者的不同處在於需不需要傳送給 Server，[Stackoverflow 的說明](https://stackoverflow.com/questions/3220660/local-storage-vs-cookies)。

> Cookies and local storage serve different purposes. Cookies are primarily for reading **server-side**, local storage can only be read by the **client-side**.

Cookie 需要傳給 Server 然後再傳回來才會顯示?
Local storage 不用傳送，可以直接顯示?

### 一閃即逝：session storage

> session : 一段時間內，連線多少次都只算一次(?)


跟 local storage 類似，只是存的位置、存的時間更短，然後從原始網頁開新分頁(A, B, C)的話，原始網頁和ABC的session storage 會共用，如果是 local storage 則會在原始網頁和不同分頁之間共用。
> 但 session storage 在 spec 上面似乎又有改了，不同瀏覽器的支援度也不一樣

> [參考+實作](https://blog.techbridge.cc/2020/09/05/session-storage-and-html-spec/)

### 三者的比較
> Cookie, local storage, session storage
![比較](https://miro.medium.com/max/1750/1*fKkGXyI5_Dg4dSslgozsoA.png)

這三個東西在購物平台之類的網站真的要處理好...不然如果使用者開很多分頁做處理，分頁之間的 cookie/local storage/ session storage 沒弄好的話，很有可能出現重複購買/取消之類的情況

## 

### 用 node.js 呼叫 API 與在網頁上呼叫的根本差異是什麼？

網頁上的 request 會經過瀏覽器這個程式的額外處理(request 可能被"加料")

### 傳送資料的第一種方式：表單 form

form 的 action 屬性會發出 request 給對方，然後收到 response 並進行動作(= 轉址)，**一定會換頁**
> POST method 會把資料放在 body(?), GET method 會把資料放在 url

### 傳送資料的第二種方式：ajax

asynchronous javascript and xml(已經變JSON了)
已經是個統稱了，以非同步的方式處理，**不會轉頁**

`request.onload = function () {}` 相當於把 request 加上onload(?)、`element.onClick = function () {}` 
![](https://i.imgur.com/YfSetCL.png)


### Same origin policy 與跨網域問題

相同協定(HTTP != HTTPs)相同主機位置相同port => 同源
ex: 同個 domain 幾乎是同源了(看網址)，如果是不同源的話，通常送出 request 之後會被瀏覽器擋下來(request/response有正常傳送，但瀏覽器不會給你看到 response)，除非對方 server 的 response 有提供 `access-control-allow-origin:` 這個 header (沒有其他解法，為了安全性，總不能隨便發 request 就能取得資料吧(?)

但是，這個同源政策是**瀏覽器**做的限制，所以用 NodeJS 發出 request 的話就不會受到它的限制

### 傳送資料的第三種方式：JSONP

SON with Padding，瀏覽器的同源政策會對 response 的處理做限制。但是!有些標籤不會受到同源政策的限制，例如 img 標籤，以及 script 標籤，瀏覽器在接收到 response body 之後會直接執行而不售同源政策的限制，所以說也可以透過這些標籤來取得資料。

限制點則是 request 的形式只能透過 GET method，因為只能透過 HTML 標籤的 url 來送出 request。

上面的方法也可以做到 email 追蹤的技術，超猛的，原理就是透過開信的時候會送出 request 取得 img src='./picture.jpg'，這樣我監視方的 server 就可以知道有誰開信(送出圖片 reuest)了。

## 實作

使用的方式是用 JS 來做動態新增的動作，稱為 client rendering，所以說檢視原始碼是看不到做變動的內容的，搜尋引擎也看不到這些

## [補充文章](https://blog.techbridge.cc/2017/05/20/api-ajax-cors-and-jsonp/)
> ~~JSNOP 後面還沒看完~~

### CORS

前面有提到 Same origin policy 這個概念，那如果想要在不同來源之間傳資料該怎麼辦 => CORS !
這個規範會在 request 加上 "Access-Control-Allow-Origin"(有其他類似的header) 這個 http header
> OS: 瀏覽器要怎麼知道甚麼時候要加上這個 header?

### Preflight Request

> **CORS 是瀏覽器在做的事情，不要忘記了**

CORS 把 request 分為兩種，簡單請求()和非簡單請求(?)，後者因為會夾帶一些使用者資料，所以會在送出 request 之前，預先送出一個 request (Preflight Request)，目的就是「透過 Preflight Request 去確認後續的請求能否送出」
> 一個重要資訊要先確認可以安全交出去，才交出去的概念?

