Web - 第二十四章 | HTML 使用JavaScript發送表單
這邊文章開始前,必須說我跳過一篇文章How to build custom form widgets,因為我這章節的內容不感興趣,而且文章太過攏長讓學習意願大大降低,所以我跳過啦。
這邊要說的文章是Sending forms through JavaScript,身為工程師就是不管前後端還是資料庫⋯等等什麼都要會要懂,這是不可避免的,而這章講到的是使用JavaScript發送表單,直覺反應就是AJAX吧~哈,下面開始進入正題。
使用JavaScript發送表單
HTML表單可以聲明式地發送一個HTTP請求。但表單也可以通過JavaScript來準備一個用於發送的HTTP請求。本文探討如何做到這一點。
一個表單並不總是一個表單
隨著開放Web應用程序的興起,使用HTML forms而不是文字表單(literal forms for humans)日益普遍-越來越多的開發人員正在控制傳輸資料。
獲得整體界面的控制
標準的HTML表單提交加載URL,這個URL是資料要發送的位置,這意味著瀏覽器窗口以整頁加載進行導航。避免整頁加載可以通過隱藏閃爍和網路滯後來提供更平滑的體驗。
許多現代用戶界面只使用HTML表單來收集用戶的輸入。當用戶嘗試發送資料時,應用程序將在後台異步控制和傳輸資料,只更新UI中需要更改的部分。
異步地發送任何資料被稱為AJAX
,代表”Asynchronous JavaScript And XML”。
表單提交和AJAX請求之間的區別?
AJAX技術主要依靠 XMLHttpRequest(XHR) DOM對象。它可以構造HTTP請求,並獲取請求結果。
注:老的AJAX技術可能不依賴XMLHttpRequest。例如, JSONP結合eval()函數。它可以使用,但是因其嚴重的安全問題不推薦使用。使用它的唯一原因是老的瀏覽器不支持XMLHttpRequest或JSON ,但是那些確實是非常古老的瀏覽器!避免使用這種技術。
建立之初, XMLHttpRequest被提出是打算將 XML 做為傳輸資料的格式。不過,JSON已經取代了XML,而且今天已經非常普遍了。
但是XML和JSON都不適合表單資料請求編碼。表單資料( application/x-www-form-urlencoded)由URL編碼的鍵/值對列表組成。為了傳輸二進制資料,HTTP請求被重新整合成multipart/form-data。
如果你控制前端(在瀏覽器中執行的代碼)和後端(在伺服器上執行的代碼),則可以發送JSON / XML並根據需要處理它們。
但是,如果你想使用第三方服務,這並不容易。有些服務只接受表單資料。也有使用表單資料更簡單的情況。如果資料是鍵/值對或原始二進制資料,現有的後端工具可以處理它,而不需要額外的代碼。
發送表單資料
一共有三種方式來發送表單資料:包括兩種傳統的方法和一種利用formData對象的新方法。
在DOM中構建一個隱藏的iframe
步發送表單資料的最古老方法是用DOM API構建表單,然後將其資料發送到隱藏的<iframe>。
警告:避免使用這項技術。有第三方服務的安全風險,因為它使你暴露在腳本注入攻擊中。如果你使用HTTPS,它可以影響同源策略,可以提供不可用的<iframe>內容。然而,該方法可能是你需要支持很古老的瀏覽器的唯一選擇。
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
| <!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="utf-8"> <title>HTML 調試示例</title> </head> <body> <button onclick="sendData({test:'ok'})">點擊我! </button> <script type="text/javascript"> var iframe = document.createElement("iframe"); iframe.name = "myTarget"; window.addEventListener("load", function () { iframe.style.display = "none"; document.body.appendChild(iframe); }); function sendData(data) { var name, form = document.createElement("form"), node = document.createElement("input"); iframe.addEventListener("load", function () { alert("Yeah! Data sent."); }); form.action = "https://morosedog.gitlab.io/"; form.target = iframe.name; for(name in data) { node.name = name; node.value = data[name].toString(); form.appendChild(node.cloneNode()); } form.style.display = "none"; document.body.appendChild(form); form.submit(); document.body.removeChild(form); } </script> </body> </html>
|
手動構建XMLHttpRequest
XMLHttpRequest是進行HTTP請求的最安全和最可靠的方式。要使用XMLHttpRequest發送表單資料,請通過對URL進行編碼來準備資料,並遵守表單資料請求的具體內容。
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
| <!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="utf-8"> <title>HTML 調試示例</title> </head> <body> <button onclick="sendData({test:'ok'})">點擊我! </button> <script type="text/javascript"> function sendData(data) { var XHR = new XMLHttpRequest(); var urlEncodedData = ""; var urlEncodedDataPairs = []; var name; for (name in data) { urlEncodedDataPairs.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name])); } urlEncodedData = urlEncodedDataPairs.join('&').replace(/%20/g, '+'); XHR.addEventListener('load', function (event) { alert('Yeah! Data sent and response loaded.'); }); XHR.addEventListener('error', function (event) { alert('Oops! Something goes wrong.'); }); XHR.open('POST', 'https://morosedog.gitlab.io/'); XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); XHR.send(urlEncodedData); } </script> </body> </html>
|
注:使用XMLHttpRequest會受到同源策略的影響,如果你需要執行跨域請求,你需要熟悉一下CORS和HTTP訪問控制。
手動建立一個HTTP請求可能是一個巨大的挑戰(can be overwhelming)。
XMLHttpRequest規範提供了一種方便簡單的方法來處理帶有FormData對象的表單資料請求。
可以使用FormData對象來構建用於傳輸的表單資料,或者獲取表單元素中的資料來管理它的發送方式。請注意,FormData 對像是“只寫”,這意味著你可以更改它們,但不檢索其內容。
使用這個對像在Using FormData Objects中有詳細的介紹,但是這裡有兩個例子:
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
| <!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="utf-8"> <title>HTML 調試示例</title> </head> <body> <button type="button" onclick="sendData({test:'ok'})">點擊我! </button> <script type="text/javascript"> function sendData(data) { var XHR = new XMLHttpRequest(); var FD = new FormData(); for(name in data) { FD.append(name, data[name]); } XHR.addEventListener('load', function(event) { alert('Yeah! Data sent and response loaded.'); }); XHR.addEventListener('error', function(event) { alert('Oups! Something goes wrong.'); }); XHR.open('POST', 'https://morosedog.gitlab.io/'); XHR.send(FD); } </script> </body> </html>
|
- 使用綁定到表單元素上的FormData
你也可以綁定一個 FormData 對像到一個 <form>元素上。這會建立一個 FormData ,代表表單中包含的元素。
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
| <!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="utf-8"> <title>HTML 調試示例</title> </head> <body> <form id="myForm"> <label for="myName">告诉我你的名字:</label> <input id="myName" name="name" value="John"> <input type="submit" value="提交"> </form> <script type="text/javascript"> window.addEventListener("load", function () { function sendData() { var XHR = new XMLHttpRequest(); var FD = new FormData(form); XHR.addEventListener("load", function(event) { alert(event.target.responseText); }); XHR.addEventListener("error", function(event) { alert('哎呀!出了問題。'); }); XHR.open("POST", "https://morosedog.gitlab.io/"); XHR.send(FD); } var form = document.getElementById("myForm"); form.addEventListener("submit", function (event) { event.preventDefault(); sendData(); }); }); </script> </body> </html>
|
發送二進制資料
如果你用來初始化formData對象的那個表單中包含了一個文件輸入框(type=file的input元素),則在發送AJAX時,用戶在這個文件輸入框中選定的文件也會被發送,和正常的表單提交一樣。而且即使你沒有用表單初始化這個formData對象,你同樣可以手動向這個formData對像中添加若干個二進制資料。
使用formData發送二進制資料非常簡單,只需要調用append方法将你需要发送的File对象或者Blob对象添加进去。
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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| <!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="utf-8"> <title>HTML 調試示例</title> </head> <body> <form id="myForm"> <p> <label for="i1">文本資料:</label> <input id="i1" name="myText" value="一些文本資料"> </p> <p> <label for="i2">文件資料:</label> <input id="i2" name="myFile" type="file"> </p> <button>提交</button> </form> <script type="text/javascript"> window.addEventListener('load', function () { var text = document.getElementById("i1"); var file = { dom : document.getElementById("i2"), binary : null }; var reader = new FileReader(); reader.addEventListener("load", function () { file.binary = reader.result; }); if(file.dom.files[0]) { reader.readAsBinaryString(file.dom.files[0]); } file.dom.addEventListener("change", function () { if(reader.readyState === FileReader.LOADING) { reader.abort(); } reader.readAsBinaryString(file.dom.files[0]); }); function sendData() { if(!file.binary && file.dom.files.length > 0) { setTimeout(sendData, 10); return; } var XHR = new XMLHttpRequest(); var boundary = "blob"; var data = ""; if (file.dom.files[0]) { data += "--" + boundary + "\r\n"; data += 'content-disposition: form-data; ' + 'name="' + file.dom.name + '"; ' + 'filename="' + file.dom.files[0].name + '"\r\n'; data += 'Content-Type: ' + file.dom.files[0].type + '\r\n'; data += '\r\n'; data += file.binary + '\r\n'; } data += "--" + boundary + "\r\n"; data += 'content-disposition: form-data; name="' + text.name + '"\r\n'; data += '\r\n'; data += text.value + "\r\n"; data += "--" + boundary + "--"; XHR.addEventListener('load', function(event) { alert('✌!資料已發送且響應已加載。'); }); XHR.addEventListener('error', function(event) { alert('哎呀!出了問題。'); }); XHR.open('POST', 'https://morosedog.gitlab.io/'); XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary); XHR.send(data); } var form = document.getElementById("myForm"); form.addEventListener('submit', function (event) { event.preventDefault(); sendData(); }); }); </script> </body> </html>
|
總結
取決於不同的瀏覽器,通過JavaScript發送資料可能會很簡單,也可能會很困難。FormData對像是通用的答案,所以請毫不猶豫的在舊瀏覽器上通過polyfill使用它。
這邊轉載了大量的文章和內容,但是每個部分我都有實際去做過,並了解過。這邊轉載主要是做筆記記錄。
註:以上參考了
MDN web docs 的 Sending forms through JavaScript 文章。