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