# [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 結果被改變

### 如何選到想要的元素: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

這三個東西在購物平台之類的網站真的要處理好...不然如果使用者開很多分頁做處理,分頁之間的 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 () {}`

### 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 去確認後續的請求能否送出」
> 一個重要資訊要先確認可以安全交出去,才交出去的概念?