---
# System prepended metadata

title: JavaScript 與瀏覽器的溝通
tags: [Lidemy]

---

# JavaScript 與瀏覽器的溝通

###### tags: `Lidemy`

以下為 Lidemy [MTR05] 課程筆記，如有錯誤，歡迎留言/寄信通知，感謝。
## DOM

在介紹 JavaScript 與瀏覽器的溝通前，要先介紹 DOM (Document Object Model)，可以把它想像從 Document 轉換成 Object 的格式，從最上層的 document，到下一層的 `<html>、<body>` 到 `<footer>` 等，猶如 Object 的包裝形式。

界面中會表示屬性與函式間的節點與物件組成的結構，將 HTML 的各標籤，像`<div>、<h1>` 等定義成物件，我們可以透過 DOM 大致看到該網頁的架構，例如其父子關係（Parent and Child），上層為 Parent Node，而下層為 Child Node；兄弟關係（Siblings），意指同一層節點的關係，分為 Previous 及 Next 兩種。 最後要補充說明即使我們常用 JavaScript 來存取 DOM，但 DOM 並非 JavaScript 的一部分。

- ### 關於事件傳遞機制的：什麼是捕獲 (Capture)？什麼又是冒泡 (Bubbling)？傳遞的順序是什麼？
指當點擊事件發生後，DOM 的執行流程。 以 [w3網頁的 DOM event flow](https://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/) 為例：
![w3DomEventFlow](https://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/images/eventflow.png)
當 Target Phase(2): 也就是`<td>` 被點擊後，紅標的 Capture Phase(1) 從 Window 開始發生，一直傳到 Document、`<html>、<body>、<table>、<tbody>、<tr>` ，此時`<tr>` 為目標 `<td>` 前一個 element，上述從 Window 到 `<tr>` 的路段，都稱為捕獲事件。 之後事件會從 Target phase(2) : `<td>` 傳回 window，與上述的路段一樣，只是順序不同，從 `<td>` 回傳到 window 的這段叫做冒泡。 因此 DOM Event flow 理論上是先捕獲，再冒泡。

- ### 什麼是 event delegation，為什麼我們需要它？
例子：
```
<div class="parent">
	<input class="child">1</input>
	<input class="child">2</input>
	<input class="child">3</input>
	<input class="total">6</input>
</div>
```
若我們要透過點擊事件，來取得每個 child 的 input.value，可以直接使用 Parent class 來綁定事件，一次取得它底下的值，而省略綁定每個 child 的值，不然就要執行該動作四次。
因此，使用 `event.delegation` 的優點是可以減少監聽事件的次數，缺點是若要選擇特定 element，則需要做判斷。例如，若只需取 child 元素中，class name 為 child 的值，就需要加上判斷，否則會連 total 的值也會拿到。

- ### 什麼是 event.preventDefault()？什麼是 event.stopPropagation()？兩者差別在哪裡？

`event.peventDefault()` 是取消瀏覽器預設行為
例如 button 的 submit 屬性，瀏覽器的預設行為是點擊後重刷頁面，但加入`event.peventDefault()`後，則會阻止其重刷頁面的動作。

`event.stopPropagation()` 是指取消後續的 DOM 行為
延續上面的 button 點擊事件，假設點擊後，捕獲時間開始，從 Window 開始發生，一直傳到 Target phase，點擊後又從 Target phase 開始執行冒泡行為，若我們不需要冒泡這段的事件，就可以使用 `event.stopPropagation`。
同樣地，若直接把 `event.stopPropagation()` 加在 windows 捕獲階段，那所有事件都不會往下傳遞。

所以，差別在於：
`event.peventDefault()` 是取消瀏覽器的預設行為，但其實整個 DOM 事件還是會繼續執行，並不會受影響；而`event.stopPropagation` 則是指取消整個後續的 DOM 行為，也是跟瀏覽器的預設行為無關。

## JavaScript 網頁事件處理（待新增）

## 如何在瀏覽器上儲存資料（待新增）

## 網頁與伺服器的溝通

網頁呼叫 API 與 Node.js 呼叫 API 的差別在於，前者透過瀏覽器發 reqeust 到 server，而瀏覽器會有安全機制，因此會擋一些東西，即便 server 回傳 response，也未必會成功回傳到 Client 端。而 Node.js 則會直接發 request 到 server。

- 傳送資料的方式：

  1. ### form 表單
     每次要新資料時，都會換頁。

  2. ### AJAX (Asynchronous JavaScript And XML)
     一種在瀏覽器上，使用 JavaScript 與 Server 端溝通的技術，首先的重點是 "A" 字，Asynchronous，非同步，什麼意思呢？簡單來說就是
     > 不須等待前面的程式執行完就能執行。
      
     詳細點來說，就要提到 JS 的運作。理論上 JS 是根據程式碼行數由上往下來執行，**但是**，若某一行的程式碼需要處理很久的話（例如發送 request 後，等待 response 的時間很長），整頁的程式都需要等候它，卡多久就等多久，整個瀏覽器都處於“凍結的狀態”；或是某一行程式有問題，瀏覽器因它而 crash 掉，但它底下的其他程式碼其實沒有問題，其他功能仍然可以正常顯示在頁面上。**這就是同步會造成的問題！** 相反地，若以非同步的形式進行，就可以避免上面的問題，若某一行的程式碼需要處理很久的話，沒關係，先跳過，繼續執行下一行；若某一行程式有問題，沒關係，其他行仍然可以執行，頁面仍會出現，除非點擊/連結到有問題的區塊，不然仍然可以正常使用頁面的其他功能。
     簡單的流程為：JS 透過瀏覽器發 request 到 Server，Server 再透過瀏覽器回傳 response 到 JS 上，有別於表單直接由瀏覽器渲染出 response 的結果。因此我們可以透過瀏覽器上的 JS，傳送及接收 Server 資料，在不更動整個頁面的情況下做不同操作。最後，"X" 所代表的是 XML，因為早期都是以 XML 作為資料傳輸的格式，然而現在常用的資料格式都以 JSON 居多，但不影響 AJAX 的運作。
     > 任何非同步，並透過 JS 來跟伺服器交換的方式都可稱為 AJAX。
     
     ### 延伸問題：用 Ajax 與我們用表單送出資料的差別在哪？
     與傳統用表單 form 傳輸資料的最大差別，就在於 "A" 字，Asynchronous，非同步，然而表單卻是同步的（synchronous）。表單在每一次的傳送資料，瀏覽器隨即會有換頁的動作，暫時無法執行其他動作。但以現今常見的網頁，使用時其實不需要動到整個頁面，而且 Server 資料眾多，若每個 request 都同步的話，這樣會花費很多時間在等待資料回傳，使用者體驗應該會十分糟糕 ~~oh no~~，這時候 AJAX 就可以大派用場。
     
     

     範例：
     ```
     const request = new XMLHttpRequest()
     
     // 也可以寫成 request.addEventListener('load', function () {})
     request.onload = function () {
         if (request.status >= 200 && request.status < 400) {
             console.log(request.responseText)
         } else {
             console.log(request.status)
         }
     }
     
     request.onerror = function() {
         console.log('error')
     }
     
     // open 即是你要發 request 到這個地方，第三個參數為是否要非同步
     request.open('GET', 'https://request.com/api/data', true)
     // 別忘了最後一步，加`send`才會真的把 request 傳出去
     request.send()
     ```
     
    - Same origin policy（同源政策） 與 跨網域問題
        - 同源也就是同 Domain，指兩份網頁具備相同協定、埠號 (如果有指定) 以及主機位置，它詳細可參考 [Mozilla](https://developer.mozilla.org/zh-TW/docs/Web/Security/Same-origin_policy)。基本上若來自不同源的網址，瀏覽器預設會擋掉對它的 response。但怎麼解決被檔的問題呢？讓我們看下去...
        - CORS (跨來源資源共用)，上述問題的答案就是使用 CORS。當我們在發 request 的時候，瀏覽器會自動在 Request Headers 裡加上一個 header，名叫 Origin，也就是你現在的 Domain，因此 Server 也是看這個 header 來判斷是否同源。 
        - Response Headers 中的 access-control-allow-origin，若 value 為 `*` 則表示所有的 origin 都能存取，若 server 沒有加上此 header，我們是絕對沒辦法拿到 response DER~ 這就是瀏覽器提供給你的限制機制。
  
  3. ### JSONP (JSON with padding)
     上述提到的問題（解決被擋），除了使用 CORS 外，另一個方法是使用 JSONP，可以讓一個網頁從其他的網域請求資料。它是利用標籤 (Tag) 來拿資料，原因是這些 Tag（如`<script>、<img>`）是不受同源政策限制，怎麼說呢？記得你曾使用過`<img scr="http://XXXX">`嗎？當中裝的連結，其實就是引用其他 Domain 的網址。同樣地，`<script src="http://XXXX">` 當中`src`中的連結，裝的也是其他 Domain 的 JavaScript，這些都是為了方便而執行的，省略從 local 端下載圖片，或是需要自己動手打一段程式碼，若它們也受同源政策管理，這樣就真的哭哭了。所以，若 `<script>` 裡有 JavaScript 的物件，我們就可以使用 JavaScript 來做相對應的操作執行。
     簡短介紹使用 JSONP 的程序：
     1. 建立一個 `<script src="xxx">` 標籤元素，當中 "xxx" 指向一個跨網域的網址。
     2. 使用 callback function 來拿資料（通常 API 上會附有 `callback` 的參數），只要把這個參數當作 function 名稱，把它的 JavaScript 物件傳到 Function 裡面，你就可以在 Function 裡面拿到資料。或是這樣說，Server 接收 request 後會回傳一個 JavaScript 的檔案，檔案中會執行 function，function 中的參數就是你想要請求的資料結果。
     3. 自行使用請求的資料結果
     
     最後補充，JSONP 也有其限制，它只能使用 GET 的方式去取 API，無法用 POST。另外，JSONP 和 JSON 沒有直接的關係，但確實會在函式中使用，利用其純文字格式方便傳輸且能支援其他的資料交換格式的好處，來想要獲取的資料。

- 單向傳送資料
只需要單向傳送，而不需要獲取 response。例子：廠商發 email 後，想要得知多少顧客點開郵件。方法就是在 email 中加了一個透明的 background 或 img，當顧客點開後就會 request 到 server，通知廠商。

- 如何存取跨領域的 API？
使用 CORS 跟 JSONP

- 為什麼我們在使用 Node.js 來傳輸 API 沒碰到跨網域的問題，使用 Web JS 卻碰到了？
這要回到所謂 API 傳輸的部分，以圖來簡略說明 ~~(這也算圖嗎喂?)~~
```
        -------(request)------->
Client                             Server
        <------(response)-------
```
以上圖來看，當我們以 Node.js 做為 Client 端，像 Server 發出 request，Server 相對地回傳 response，當中並無任何限制，十分直接地一來一往。
然而，當我們用瀏覽器的 JS 來傳輸時，圖會變為：
```  
        --------------- > Browser ---(request)--->
Client                                             Server
        <---(response)--- Browser <---------------
```
顯而易見，中間多了一個第三者從中作梗！~~(可惡)~~，沒錯，它就是瀏覽器。
此時，我們的傳輸路徑多了瀏覽器這個關卡，這個關卡有兩個主要項目（都是與資訊安全性有關），一是會限制一些操作，二是幫你 Client 加上一些背景資料，讓 Server 知道來者何人。但也是因為這兩個項目，導致你的傳輸受限，無法直接地獲取資訊，並需要額外運用一些功能來傳輸。