簡介 名詞
Client-side Render(CSR) : API get data 再由 JavaScript 渲染
Server-side Render(SSR) : 在 Server-side 就先渲染成 HTML
Search Engine Optimization(SEO) : 搜尋引擎最佳化,透過了解搜尋引擎的運作規則來調整網站,以及提高目的網站在有關搜尋引擎內排名的方式
JavaScript 的功能
介面 : 改變介面
事件 : 監聽事件並做出反應
資料 : 與伺服器交換資料
執行 放於 body 最後 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .box { background : orange; height : 100px ; width : 100px ; } </style > </head > <body > <div class ="box" > </div > <script > document .querySelector('.box' ) .addEventListener('click' , function ( ) { document .querySelector('.box' ).style.background = 'red' }) </script > </body > </html >
放於 head 最後, 但要加入 DOM loaded(因執行時 DOM 尚未 load 完成) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html > <html lang ="en" > <head > ...... <script > document .addEventListener('DOMContentLoaded' , function ( ) { document .querySelector('.box' ) .addEventListener('click' , function ( ) { document .querySelector('.box' ).style.background = 'red' }) }) </script > </head > <body > <div class ="box" > </div > </body > </html >
head 加入js 檔 1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > ...... <script src ="main.js" > </script > <title > Document</title > </head > <body > <div class ="box" > </div > </body > </html >
1 2 3 4 5 6 7 8 document .addEventListener('DOMContentLoaded' , function ( ) { document .querySelector('.box' ) .addEventListener('click' , function ( ) { document .querySelector('.box' ).style.background = 'red' }) })
文件物件模型(Document Object Model, DOM)是 HTML、XML 和 SVG 文件的程式介面。它提供了一個文件(樹)的結構化表示法,並定義讓程式可以存取並改變文件架構、風格和內容的方法。
圖片來自 Wikipedia DOM
選元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .box { background : orange; height : 100px ; width : 100px ; } </style > </head > <body > <div id ="item1" class ="box" > </div > <div class ="box box2" > </div > <div class ="box" > </div > <script > const elements = document .getElementsByTagName('div' ) console .log(elements) console .log(document .getElementsByClassName('box' )) console .log(document .getElementById('item1' )) console .log(document .querySelector('div' )) console .log(document .querySelector('.box2' )) console .log(document .querySelector('#item1' )) const elementsNode = document .querySelectorAll('div' ) console .log(elementsNode) </script > </body > </html >
.parentElement parentElement is the parent element of the current node. 若無父元素或非element則傳回 null
.parentNode parentNode is the parent of the current node. The parent of an element is an Element node, a Document node, or a DocumentFragment node. 若無父元素或非element則傳回 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .box { background : orange; width : 100px ; } </style > </head > <body > <div id ="item1" class ="box" > 111</div > <div class ="box box2" > 222</div > <div class ="box" > 333</div > <script > const element = document .querySelector('.box2' ) element.style.background = "red" element.style.margin = "10px" element.style['padding-top' ] = '10px' element.style.paddingBottom = '20px' </script > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .box { background : orange; width : 100px ; height : 100px ; } .active { background : red; } </style > </head > <body > <div id ="item1" class ="box" > 111</div > <div class ="box box2 active" > 222</div > <div class ="box box3" > 333</div > <script > document .querySelector('#item1' ) .addEventListener('click' , function ( ) { document .querySelector('#item1' ).classList.toggle('active' ) }) document .querySelector('.box2' ) .addEventListener('click' , function ( ) { document .querySelector('.box2' ).classList.remove('active' ) }) document .querySelector('.box3' ) .addEventListener('click' , function ( ) { document .querySelector('.box3' ).classList.add('active' ) }) </script > </body > </html >
check 是否包含某class 1 alert(div.classList.contains("foo" ));
改變內容 innerText, innerHTML, outerHTML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .box { background : orange; width : 200px ; font-size : 36px ; } .active { background : red; } </style > </head > <body > <div id ="item1" class ="box" > 111 <a href ="#" > hello</a > </div > <div class ="box box2 active" > 222 <a href ="#" > hello</a > </div > <div class ="box box3" > 333 <a href ="#" > hello</a > </div > <script > document .querySelector('#item1' ) .addEventListener('click' , function ( ) { element = document .querySelector('#item1' ) console .log(element.innerText) element.innerText = 'innerText' }) document .querySelector('.box2' ) .addEventListener('click' , function ( ) { element = document .querySelector('.box2' ) console .log(element.innerHTML) element.innerText = 'innerHTML' }) document .querySelector('.box3' ) .addEventListener('click' , function ( ) { element = document .querySelector('.box3' ) console .log(element.outerHTML) element.outerHTML = 'outerHTML' }) </script > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // use innerHTML sendAjaxhApi('http://localhost/robert/comment/api_get_comment.php', function(response){ const comments = JSON.parse(response) console.log(comments.comment[0]) let section = document.querySelector('section') for(let i=0 ; i < comments.comment.length ; i++ ) { let card = document.createElement('div') card.classList.add('card') card.innerHTML = ` <div class ="card-avatar" > </div > <div class ="card-info" > <div class ="card-title" > <span class ="card-user" > ${comments.comment[i].nickname}</span > <span class ="card-time" > ${comments.comment[i].create_at}</span > <div class ="card-content" > ${comments.comment[i].content} </div > </div > ` section.appendChild(card) } })
加入刪除 元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .box { background : orange; width : 200px ; font-size : 36px ; } </style > </head > <body > <div class ="box" > 111 <a href ="#" > hello</a > </div > <script > element = document .querySelector('.box' ) element.removeChild(document .querySelector('a' )) const item = document .createElement('div' ) item.innerText = '123' element.appendChild(item) const itemText = document .createTextNode("999" ) element.appendChild(itemText) </script > </body > </html >
.append() 加入Node物件或DOMString(文字)
1 2 3 4 5 6 7 const parent = document .createElement('div' )const child = document .createElement('p' )parent.append(child) const parent = document .createElement('div' )parent.append('附加文字' )
.appendChild() 僅能加入Node物件, 不能加入DOMString(文字) 若加入的物件已存在,則會移動到此
1 2 3 4 5 6 7 const parent = document .createElement('div' )const child = document .createElement('p' )parent.appendChild(child) const parent = document .createElement('div' )parent.appendChild('Appending Text' )
1 2 var el = document .getElementById('div-02' );el.remove();
addEventListener 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .box1 { background : orange; width : 200px ; font-size : 36px ; margin : 5px 0 ; } .box2 { background : orange; width : 200px ; font-size : 36px ; } </style > </head > <body > <div class ="box1" > 111 <a href ="#" > hello</a > </div > <div class ="box2" > 222 </div > <input class ="input-text" type ="text" > <script > const element = document .querySelector('.box1' ) element.addEventListener('click' , handleBox1); function handleBox1 (e ) { alert('Box1' ) console .log(e.target) } document .querySelector('.box2' ) .addEventListener('click' , function ( ) { alert('Box2' ) }) document .querySelector('.input-text' ) .addEventListener('keypress' , function (e ) { console .log(e) console .log(e.key, e.keyCode, e.code) }) </script > </body > </html >
click mousedown mouseup dbclick - 短時間內雙擊左鍵觸發 mousemove - 滑鼠移動時觸發(要用到時才綁定,避免不斷觸發) mouseenter - 滑鼠進入元素邊界時觸發(不會 bubble) mouseleave - 滑鼠完全離開元素時觸發(不會 bubble) mouseover - 滑鼠經過不同元素時觸發 mouseout - 滑鼠離開元素時觸發 keypress
1 2 3 4 /* e.key : 字元 e.keyCode : 編碼 ascii code e.code : 字串表示 "KeyQ" for the Q */
keydown keyup DOMContentLoaded - DOM load complete 1 2 3 4 document .addEventListener('DOMContentLoaded' , function ( ) { ...... })
表單處理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > body { font-size : 24px ; } </style > </head > <body > <form class ="login-form" action ="" > <div > name : <input type ="text" name ="username" > </div > <div > password : <input type ="password" name ="password" > </div > <div > password again : <input type ="password" name ="password2" > </div > <input type ="submit" value ="送出" > </form > <script > document .querySelector('.login-form' ) .addEventListener('submit' , function (e ) { const input1 = document .querySelector('input[name=password]' ) const input2 = document .querySelector('input[name=password2]' ) console .log(input1.value, input2.value) if (input1.value != input2.value) { e.preventDefault() alert('密碼不同' ) } }) </script > </body > </html >
阻止預設行為 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > body { font-size : 24px ; } </style > </head > <body > <div > 111 <a href ="#" > hello</a > </div > <script > document .querySelector('a' ).addEventListener('click' , function (e ) { e.preventDefault(); }) </script > </body > </html >
事件傳遞機制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .outer { width : 200px ; height : 200px ; background : yellow; } .inner { width : 100px ; height : 100px ; background : orange; } </style > </head > <body > <div class ="outer" > <div class ="inner" > <button class ="button" > Click</button > </div > </div > <script > addEvent('.outer' ) addEvent('.inner' ) addEvent('.button' ) function addEvent (className ) { document .querySelector(className) .addEventListener('click' , function ( ) { console .log(className) }) } </script > </body > </html >
詳細事件傳遞機制:捕獲與冒泡
圖片來自 W3C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .outer { width : 200px ; height : 200px ; background : yellow; } .inner { width : 100px ; height : 100px ; background : orange; } </style > </head > <body > <div class ="outer" > <div class ="inner" > <button class ="button" > Click</button > </div > </div > <script > addEvent('.button' ) addEvent('.inner' ) addEvent('.outer' ) function addEvent (className ) { document .querySelector(className) .addEventListener('click' , function ( ) { console .log(className, '捕獲' ) }, true ) document .querySelector(className) .addEventListener('click' , function ( ) { console .log(className, '冒泡' ) }, false ) } </script > </body > </html >
別向上級回報:stopPropagation() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .outer { width : 200px ; height : 200px ; background : yellow; } .inner { width : 100px ; height : 100px ; background : orange; } </style > </head > <body > <div class ="outer" > <div class ="inner" > <button class ="button" > Click</button > </div > </div > <script > addEvent('.button' ) addEvent('.inner' ) addEvent('.outer' ) function addEvent (className ) { document .querySelector(className) .addEventListener('click' , function (e ) { console .log(className, '捕獲' ) }, true ) document .querySelector(className) .addEventListener('click' , function (e ) { if (className === '.button' ) { e.stopPropagation() } console .log(className, '冒泡' ) }, false ) } </script > </body > </html >
多重觸發
1 2 3 4 5 6 7 8 9 10 11 <script > document .querySelector('.button' ) .addEventListener('click' , function (e ) { console .log("button click 1" ) }) document .querySelector('.button' ) .addEventListener('click' , function (e ) { console .log("button click 2" ) }) </script >
抑制後觸發
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script > document .querySelector('.button' ) .addEventListener('click' , function (e ) { e.stopImmediatePropagation() console .log("button click 1" ) }) document .querySelector('.button' ) .addEventListener('click' , function (e ) { console .log("button click 2" ) }) </script >
錯誤的 event for 使用 var 為錯誤, let 即正確
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .btn { font-size : 24px ; } </style > </head > <body > <div class ="outer" > <button class ="btn" > 1</button > <button class ="btn" > 2</button > <button class ="btn" > 3</button > <button class ="btn" > 4</button > <button class ="btn" > 5</button > </div > <script > const elements = document .querySelectorAll('.btn' ) for (let i=0 ; i< elements.length ; i++) { elements[i].addEventListener('click' , function ( ) { alert(i+1) }) } </script > </body > </html >
使用 html data-xx attribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .btn { font-size : 24px ; } </style > </head > <body > <div class ="outer" > <button class ="btn" data-value ="a" > 1</button > <button class ="btn" data-value ="2" > 2</button > <button class ="btn" data-value ="3" > 3</button > <button class ="btn" data-value ="4" > 4</button > <button class ="btn" data-value ="5" > 5</button > </div > <script > const elements = document .querySelectorAll('.btn' ) for (let i=0 ; i< elements.length ; i++) { elements[i].addEventListener('click' , function (e ) { alert(e.target.getAttribute('data-value' )) }) } </script > </body > </html >
add new button
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .add-btn, .btn { font-size : 24px ; } </style > </head > <body > <div class ="outer" > <button class ="add-btn" > add</button > <button class ="btn" data-value ="1" > 1</button > <button class ="btn" data-value ="2" > 2</button > </div > <script > let num = 3 const elements = document .querySelectorAll('.btn' ) for (let i=0 ; i< elements.length ; i++) { elements[i].addEventListener('click' , function (e ) { alert(e.target.getAttribute('data-value' )) }) } document .querySelector('.add-btn' ) .addEventListener('click' , function ( ) { const btn = document .createElement('button' ) btn.classList.add('btn' ) btn.setAttribute('data-value' , num) btn.innerText = num++ document .querySelector('.outer' ).appendChild(btn) }) </script > </body > </html >
event delegation(代理) 事件代理(Event Delegation),又稱之為事件委託
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .add-btn, .btn { font-size : 24px ; } </style > </head > <body > <div class ="outer" > <button class ="add-btn" > add</button > <button class ="btn" data-value ="1" > 1</button > <button class ="btn" data-value ="2" > 2</button > </div > <script > let num = 3 document .querySelector('.add-btn' ) .addEventListener('click' , function ( ) { const btn = document .createElement('button' ) btn.classList.add('btn' ) btn.setAttribute('data-value' , num) btn.innerText = num++ document .querySelector('.outer' ).appendChild(btn) }) document .querySelector('.outer' ) .addEventListener('click' , function (e ) { if (e.target.classList.contains('btn' )) { alert(e.target.getAttribute('data-value' )) } }) </script > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > .item { border : 1px solid #000 ; } </style > </head > <body > <ul class ="list" > <li class ="item" id ="id1" > item1</li > <li class ="item" > item2</li > <li class ="item" > item3</li > </ul > <script > document .addEventListener('click' , function (e ) { let element = e.target if (element.classList.contains('item' )) { console .log(element.innerText) } if (element.id === "id1" ) { console .log(element.id) } }) </script > </body > </html >
DOM loaded 1 2 3 4 document .addEventListener('DOMContentLoaded' , function ( ) { ...... })
Element Attribute 1 2 3 4 5 6 7 8 9 var div1 = document .getElementById("div1" );var align = div1.getAttribute("align" );var b = document .querySelector("button" );b.setAttribute("name" , "helloButton" ); b.setAttribute("disabled" , "" );
1 2 3 4 5 document .getElementById("check1" ).checked=true if (document .getElementById("check1" ).checked){ consloe.log('It' s selected.) }
parentElement 父元素 1 var x = document .getElementById("myLI" ).parentElement
1 2 document .querySelector('.navbar a' ).click()
.target - 初觸發事件的物件 tag name 1 2 3 4 5 document .querySelector('.box' ) .addEventListener('click' , function (e ) { console .log(e.target.tagName.toLowerCase()) })
.currentTarget - 處理事件之監聽器所屬的物件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const request = new XMLHttpRequest()request.onload = function ( ) { if (request.status >= 200 && request.status < 400 ) { console .log(request.responseText) } else { console .log('response error :' , request.status) } } request.onerror = function ( ) { console .log('error' ) } request.open('GET' , 'https://google.com' , true ) request.send()
1 2 3 let searchParams = new URLSearchParams(window .location.search);let id = searchParams.get('id' );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 window .history.back();window .history.forward();window .history.go(-1 );window .history.go(1 );history.pushState(null , null , '/hello' ) pushState() replaceState() popstate 事件
fetch and promise fetch API Fetch API 提供了一個能獲取包含跨網路資源在的資源介面,它有點像我們所熟悉的 XMLHttpRequest,回傳為 Promise 的物件
fetch example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 const result = fetch('https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' )console .log(result)function printResult (resp ) { console .log(resp) } const result = fetch('https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' )result.then(printResult) api500 = "https://run.mocky.io/v3/db4c7417-2cf8-473e-9f05-f16ec2cfe725" api200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' fetch(api500).then( response => { console .log(response.status) }) fetch(api200).then( response => { console .log(response.status) }) api200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' fetch(api200).then( response => { response.text().then( text => { console .log(text) }) }) api200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' fetch(api200).then( response => { response.json().then(text => { console .log(text) console .log(text.message) return 123 }).then( abc => { console .log('abc=' , abc) }) }) api200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' fetch(api200).then( response => { return response.json() }).then(json => { console .log(json) }) api200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047-err' fetch(api200).then( response => { return response.json() }).then(json => { console .log(json) }).catch(e => { console .log("error=" , e) }) data = { age: 30 } pi200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' fetch(pi200, { body: JSON .stringify(data), headers: { 'content-type' : 'application/json' }, method: 'POST' , }).then(response => { return response.json() }).then( json => { console .log(json) })
fetch 注意事項
content-type 要填正確,如以下為 json
1 2 3 4 5 6 7 8 9 10 11 12 pi200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' fetch(pi200, { body: JSON .stringify(data), headers: { 'content-type' : 'application/json' }, method: 'POST' , }).then(response => { return response.json() }).then( json => { console .log(json) }
fetch 不會傳送 cookies,除非你有設定 credential
要讓瀏覽器將 credentials 跟著 request 一起送出, 方式就是在 init object 加上 credentials: ‘include’
想要把 credentials 發送給同源的 URL ,加上credentials: ‘same-origin’。
要確保瀏覽器不會帶著 credentials 請求,可以用 credentials: ‘omit’ 。
1 2 3 4 5 6 7 8 9 10 11 fetch('https://example.com' , { credentials: 'include' }) fetch('https://example.com' , { credentials: 'same-origin' }) fetch('https://example.com' , { credentials: 'omit' })
mode: ‘no-cors’僅告訴 server, don’t card CPRS don’n need response error,並不表示server 不support時,能收到資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 data = { age: 30 } pi200 = 'https://tw.yahoo.com' fetch(pi200, { body: JSON .stringify(data), headers: { 'content-type' : 'application/json' }, method: 'POST' , mode: 'no-cors' , }).then(response => { return response.text() .then( text => { console .log(text) }) })
Promise Promise應用 clipboard 1 2 3 4 5 var promise = navigator.clipboard.readText()promise.then(data => { console .log() })
Promise example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 const myPromise = new Promise ( (resolve, reject ) => { resolve(4 ) }) myPromise.then( data => { console .log('data=' , data) }).catch(err => { console .log('error=' , err) }) const myPromise = new Promise ( (resolve, reject ) => { setTimeout (() => { resolve(6 ) }, 3000 ) }) myPromise.then( data => { console .log('data=' , data) }).catch(err => { console .log('error=' , err) }) api200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' const myPromise = new Promise ((resolve, reject ) => { var request = new XMLHttpRequest() request.open('GET' , api200, true ) request.onload = function ( ) { if (this .status >= 200 && this .status < 400 ) { var data = JSON .parse(this .response) resolve(data) } } request.onerror = function (err ) { reject(err) } request.send() }) myPromise.then( data => { console .log('myPromise data =' , data) }).catch(err => { console .log('error =' , err) }) const sleep = ms => new Promise (resolve => setTimeout (resolve, ms)) sleep(1500 ).then( data => { console .log('myPromise data' , data) }) .catch (err => { console .log('err =' , err) })
async and await
await 特性下,會等 promise 任務完成後才會讓程式繼續往下執行
async,他能夠將 await 包在裡面,被包在裡面的 await 會依序地執行
example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const response = await fetch(...)console .log(response)const sleep = ms => new Promise (resolve => setTimeout (resolve, ms)) async function main ( ) { console .log('enter main' ) await sleep(1000 ) console .log('exit main' ) } main() const sleep = ms => new Promise (resolve => setTimeout (resolve, ms)) function getData ( ) { const api200 = 'https://run.mocky.io/v3/d2268e25-bfa4-4e70-a505-b5faf61f7047' return fetch(api200) .then( response => { return response.json() }).then(json => { console .log(json) }) } async function main ( ) { console .log('enter main' ) await sleep(1000 ) try { const result = await getData() } catch (err) { console .log(err) } console .log('exit main' ) } main()
瀏覽器上儲存資料 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > </style > </head > <body > <div > <div > id : <input class ="id" type ="text" > </div > <div > phone : <input class ="phone" type ="text" > </div > <button class ="btn-save" > 儲存</button > </div > <script > function setCookie (cname, cvalue, exdays ) { let d = new Date () d.setTime(d.getTime() + exdays*24*60*60*1000) let expires = 'expires=' + d.toGMTString() document .cookie = cname + '=' + cvalue + ';' + expires + ';path=/' ; } function getCookie (cname ) { let name = cname + '=' let decodeCookie = decodeURIComponent (document .cookie) let ca = decodeCookie.split(';' ) for (let i=0 ; i < ca.length ; i++) { let c = ca[i] c = c.trimStart(c) if (c.indexOf(name) === 0) { return c.substring(name.length, c.length) } } return '' } const oldId = getCookie('user-id' ) document .querySelector('.id' ).value = oldId document .querySelector('.phone' ).value = getCookie('phone' ) document .querySelector('.btn-save' ) .addEventListener('click' , function ( ) { const id = document .querySelector('.id' ).value setCookie('user-id' , id, 7 ) setCookie('phone' , document .querySelector('.phone' ).value, 7 ) }) </script > </body > </html >
儲存與 server 無關資料
simple example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > </style > </head > <body > <div > id : <input class ="id" type ="text" > <button class ="btn-save" > 儲存</button > </div > <script > const oldId = window .localStorage.getItem('id' ) document .querySelector('.id' ).value = oldId document .querySelector('.btn-save' ) .addEventListener('click' , function ( ) { const id = document .querySelector('.id' ).value window .localStorage.setItem('id' , id) }) </script > </body > </html >
save job list 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > </style > </head > <body > <div > <div > job : <input class ="job" type ="text" > <button class ="btn-add" > 新增</button > </div > <div class ="list" > </div > </div > <script > let jobList = JSON .parse(window .localStorage.getItem('todo-list' )) let idIndex = Number (JSON .parse(window .localStorage.getItem('id-index' ))) if (jobList === null ) { jobList = [] } if (idIndex === 0) { idIndex++ } showList(jobList) document .querySelector('.btn-add' ) .addEventListener('click' , function ( ) { let item = {} item.job = document .querySelector('.job' ).value if (item.job !== '' ) { item.id = idIndex++ jobList.push(item) let jobListJson = JSON .stringify(jobList) window .localStorage.setItem('todo-list' , jobListJson) window .localStorage.setItem('id-index' , String (idIndex)) showList(jobList) } else { console .log('Input error' ) } }) function showList (job ) { const listDiv = document .querySelector('.list' ) listDiv.innerText = '' for (let i=0 ; i < jobList.length ; i++) { let item = document .createElement('div' ) item.innerText = `${job[i].id} : ${job[i].job} ` listDiv.appendChild(item) } } </script > </body > </html >
session storage 網頁關掉即清除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > <style > </style > </head > <body > <div > id : <input class ="id" type ="text" > <button class ="btn-save" > 儲存</button > </div > <script > const oldId = window .sessionStorage.getItem('id' ) document .querySelector('.id' ).value = oldId document .querySelector('.btn-save' ) .addEventListener('click' , function ( ) { const id = document .querySelector('.id' ).value window ,sessionStorage.setItem('id' , id) }) </script > </body > </html >
傳送資料 表單 from –> Browser 畫面更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <div class ="app" > <form method ="POST" action ="https://google.com" > username: <input type ="text" name ="username" > <input type ="submit" > </form > </div > </body > </html >
AJAX(Asynchronous JavaScript and XML) –> 資料回傳至 JavaScript AJAX google - 瀏覽器的限制 同源策略(Same-origin policy) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <script > const request = new XMLHttpRequest() request.onload = function ( ) { if (request.status >= 200 && request.status < 400) { console .log(request.responseText) } else { console .log('response error :' , request.status) } } request.onerror = function ( ) { console .log('error' ) } request.open('GET' , 'https://google.com' , true ) request.send() </script > </body > </html >
AJAX fake data - Access-Control-Allow-Origin : * CORS(全名為 Cross-Origin Resource Sharing) 跨來源資源共享 : server response head 加 Access-Control-Allow-Origin : *
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <meta http-equiv ="X-UA-Compatible" content ="ie=edge" > <title > Document</title > </head > <body > <script > const request = new XMLHttpRequest() request.onload = function ( ) { if (request.status >= 200 && request.status < 400) { console .log(request.responseText) } else { console .log('response error :' , request.status) } } request.onerror = function ( ) { console .log('error' ) } request.open('GET' , 'https://reqres.in/api/users/2' , true ) request.send() </script > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function sendTwitchApi (url, respProcess ) { const request = new XMLHttpRequest() request.onload = function ( ) { if (request.status >= 200 && request.status < 400 ) { respProcess(request.responseText) } else { console .log('response :' , request) console .log('response error :' , request.status) } } request.onerror = function ( ) { console .log('error' ) } request.open('GET' , url, true ) request.setRequestHeader('Accept' ,'application/vnd.twitchtv.v5+json' ); request.setRequestHeader('Client-ID' ,'qvtuq71csrlv5ipxo1ljzgbzqn1okh' ); request.send() }
JSONP(JSON with Padding) 第三種方式資料交換方式
img, scrip不受同源限制
使用 script 傳回 json data 即可不受 同源策略 限制
若 json 外部可包一個 function 即可方便處理,故要 server 配合
某些 server 可指定 callback function(如 Twitch)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script > function receiveData (response ) { console .log(response); } </script > <script > receiveData( { name: 'Robert' , age: 30 } ) </script >
單向資料傳送(email 或 廣告 追蹤) 擺一張小而透明的檔案,若網頁被打開,server就知道 email/廣告 被打開
Window method alert() Displays an alert dialog
1 2 3 window .alert("hello!" )alert("hello2 !" )
load 畫面 1 2 3 4 location.reload() window .location = 'index.html?id=' + respId
window load complete 1 2 3 window .onload = function ( ) { ...... }
Snippet for JavaScript escape HTML 1 2 3 4 5 6 7 function escapeHTML (s ) { return s.replace(/&/g , '&' ) .replace(/"/g , '"' ) .replace(/</g , '<' ) .replace(/>/g , '>' ) .replace(/'/g , "'" ); }