摘要:版本號(hào)后面是響應(yīng)狀態(tài),首先是一個(gè)三位的狀態(tài)碼,然后是一個(gè)可讀的字符串。以開頭的狀態(tài)碼表示請(qǐng)求成功。是最著名的狀態(tài)碼了,表示找不到資源。調(diào)用返回一個(gè),它解析為一個(gè)對(duì)象,該對(duì)象包含服務(wù)器響應(yīng)的信息,例如狀態(tài)碼和協(xié)議頭。
來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:HTTP and Forms
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
自豪地采用谷歌翻譯
部分參考了《JavaScript 編程精解(第 2 版)》
通信在實(shí)質(zhì)上必須是無狀態(tài)的,從客戶端到服務(wù)器的每個(gè)請(qǐng)求都必須包含理解請(qǐng)求所需的所有信息,并且不能利用服務(wù)器上存儲(chǔ)的任何上下文。
Roy Fielding,《Architectural Styles and the Design of Network-based Software Architectures》
我們?cè)诘?13 章中提到過超文本傳輸協(xié)議(HTTP),萬維網(wǎng)中通過該協(xié)議進(jìn)行數(shù)據(jù)請(qǐng)求和傳輸。在本章中會(huì)對(duì)該協(xié)議進(jìn)行詳細(xì)介紹,并解釋瀏覽器中 JavaScript 訪問 HTTP 的方式。
協(xié)議當(dāng)你在瀏覽器地址欄中輸入eloquentjavascript.net/18_http.html時(shí),瀏覽器會(huì)首先找到和eloquentjavascript.net相關(guān)的服務(wù)器的地址,然后嘗試通過 80 端口建立 TCP 連接,其中 80 端口是 HTTP 的默認(rèn)通信端口。如果該服務(wù)器存在并且接受了該連接,瀏覽器可能發(fā)送如下內(nèi)容。
GET /18_http.html HTTP/1.1 Host: eloquentjavascript.net User-Agent: Your browser"s name
然后服務(wù)器會(huì)通過同一個(gè)鏈接返回如下內(nèi)容。
HTTP/1.1 200 OK Content-Length: 65585 Content-Type: text/html Last-Modified: Mon, 08 Jan 2018 10:29:45 GMT ... the rest of the document
瀏覽器會(huì)選取空行之后的響應(yīng)部分,也就是正文(不要與 HTML 標(biāo)簽混淆),并將其顯示為 HTML 文檔。
由客戶端發(fā)出的信息叫作請(qǐng)求。請(qǐng)求的第一行如下。
GET /17_http.html HTTP/1.1
請(qǐng)求中的第一個(gè)單詞是請(qǐng)求方法。GET表示我們希望得到一個(gè)我們指定的資源。其他常用方式還有DELETE,用于刪除一個(gè)資源;PUT用于替換資源;POST用于發(fā)送消息。需要注意的是服務(wù)器并不需要處理所有收到的請(qǐng)求。如果你隨機(jī)訪問一個(gè)網(wǎng)站并請(qǐng)求刪除主頁,服務(wù)器很有可能會(huì)拒絕你的請(qǐng)求。
方法名后的請(qǐng)求部分是所請(qǐng)求的資源的路徑。在最簡(jiǎn)單的情況下,一個(gè)資源只是服務(wù)器中的一個(gè)文件。不過,協(xié)議并沒有要求資源一定是實(shí)際文件。一個(gè)資源可以是任何可以像文件一樣傳輸?shù)臇|西。很多服務(wù)器會(huì)實(shí)時(shí)地生成這些資源。例如,如果你打開github.com/marijnh,服務(wù)器會(huì)在數(shù)據(jù)庫中尋找名為marijnjh的用戶,如果找到了則會(huì)為該用戶的生成介紹頁面。
請(qǐng)求的第一行中位于資源路徑后面的HTTP/1.1用來表明所使用的 HTTP 協(xié)議的版本。
在實(shí)踐中,許多網(wǎng)站使用 HTTP v2,它支持與版本 1.1 相同的概念,但是要復(fù)雜得多,因此速度更快。 瀏覽器在與給定服務(wù)器通信時(shí),會(huì)自動(dòng)切換到適當(dāng)?shù)膮f(xié)議版本,并且無論使用哪個(gè)版本,請(qǐng)求的結(jié)果都是相同的。 由于 1.1 版更直接,更易于使用,因此我們將專注于此。
服務(wù)器的響應(yīng)也是以版本號(hào)開始的。版本號(hào)后面是響應(yīng)狀態(tài),首先是一個(gè)三位的狀態(tài)碼,然后是一個(gè)可讀的字符串。
HTTP/1.1 200 OK
以 2 開頭的狀態(tài)碼表示請(qǐng)求成功。以 4 開頭的狀態(tài)碼表示請(qǐng)求中有錯(cuò)誤。404 是最著名的 HTTP 狀態(tài)碼了,表示找不到資源。以 5 開頭的狀態(tài)碼表示服務(wù)器端出現(xiàn)了問題,而請(qǐng)求沒有問題。
請(qǐng)求或響應(yīng)的第一行后可能會(huì)有任意個(gè)協(xié)議頭,多個(gè)形如name: value的行表明了和請(qǐng)求或響應(yīng)相關(guān)的更多信息。這些是示例響應(yīng)中的頭信息。
Content-Length: 65585 Content-Type: text/html Last-Modified: Thu, 04 Jan 2018 14:05:30 GMT
這些信息說明了響應(yīng)文檔的大小和類型。在這個(gè)例子中,響應(yīng)是一個(gè) 65585 字節(jié)的 HTML 文檔,同時(shí)也說明了該文檔最后的更改時(shí)間。
多數(shù)大多數(shù)協(xié)議頭,客戶端或服務(wù)器可以自由決定需要在請(qǐng)求或響應(yīng)中包含的協(xié)議頭,不過也有一些協(xié)議頭是必需的。例如,指明主機(jī)名的Host頭在請(qǐng)求中是必須的,因?yàn)橐粋€(gè)服務(wù)器可能在一個(gè) IP 地址下有多個(gè)主機(jī)名服務(wù),如果沒有Host頭,服務(wù)器則無法判斷客戶端嘗試請(qǐng)求哪個(gè)主機(jī)。
請(qǐng)求和響應(yīng)可能都會(huì)在協(xié)議頭后包含一個(gè)空行,后面則是消息體,包含所發(fā)送的數(shù)據(jù)。GET和DELETE請(qǐng)求不多帶帶發(fā)送任何數(shù)據(jù),但PUT和POST請(qǐng)求則會(huì)。同樣地,一些響應(yīng)類型(如錯(cuò)誤響應(yīng))不需要有消息體。
瀏覽器和 HTTP正如上例所示,當(dāng)我們?cè)跒g覽器地址欄輸入一個(gè) URL 后瀏覽器會(huì)發(fā)送一個(gè)請(qǐng)求。當(dāng) HTML 頁面中包含有其他的文件,例如圖片和 JavaScript 文件時(shí),瀏覽器也會(huì)一并獲取這些資源。
一個(gè)較為復(fù)雜的網(wǎng)站通常都會(huì)有 10 到 200 個(gè)不等的資源。為了可以很快地取得這些資源,瀏覽器會(huì)同時(shí)發(fā)送多個(gè)GET請(qǐng)求,而不是一次等待一個(gè)請(qǐng)求。此類文檔都是通過GET方法來獲取的。
HTML頁面可能包含表單,用戶可以在表單中填入一些信息然后由瀏覽器將其發(fā)送到服務(wù)器。如下是一個(gè)表單的例子。
這段代碼描述了一個(gè)有兩個(gè)輸入字段的表單:較小的輸入字段要求用戶輸入姓名,較大的要求用戶輸入一條消息。當(dāng)點(diǎn)擊發(fā)送按鈕時(shí),表單就提交了,這意味著其字段的內(nèi)容被打包到 HTTP 請(qǐng)求中,并且瀏覽器跳轉(zhuǎn)到該請(qǐng)求的結(jié)果。
當(dāng)元素的method屬性是GET(或省略)時(shí),表單中的信息將作為查詢字符串添加到action URL 的末尾。 瀏覽器可能會(huì)向此 URL 發(fā)出請(qǐng)求:
GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1
問號(hào)表示路徑的末尾和查詢字符串的起始。后面是多個(gè)名稱和值,這些名稱和值分別對(duì)應(yīng)form輸入字段中的name屬性和這些元素的內(nèi)容。&字符用來分隔不同的名稱對(duì)。
在這個(gè) URL 中,經(jīng)過編碼的消息實(shí)際原本是"Yes?",只不過瀏覽器用奇怪的代碼替換了問號(hào)。我們必須替換掉請(qǐng)求字符串中的一些字符。使用%3F替換的問號(hào)就是其中之一。這樣看,似乎有一個(gè)不成文的規(guī)定,每種格式都會(huì)有自己的轉(zhuǎn)義字符。這里的編碼格式叫作 URL 編碼,使用一個(gè)百分號(hào)和16進(jìn)制的數(shù)字來對(duì)字符進(jìn)行編碼。在這個(gè)例子中,3F(十進(jìn)制為 63)是問號(hào)字符的編碼。JavaScript 提供了encodeURIComponent和decodeURIComponent函數(shù)來按照這種格式進(jìn)行編碼和解碼。
console.log(encodeURIComponent("Yes?")); // → Yes%3F console.log(decodeURIComponent("Yes%3F")); // → Yes?
如果我們將本例 HTML 表單中的method屬性更改為POST,則瀏覽器會(huì)使用POST方法發(fā)送該表單,并將請(qǐng)求字符串放到請(qǐng)求正文中,而不是添加到 URL 中。
POST /example/message.html HTTP/1.1 Content-length: 24 Content-type: application/x-www-form-urlencoded name=Jean&message=Yes%3F
GET請(qǐng)求應(yīng)該用于沒有副作用的請(qǐng)求,而僅僅是詢問信息。 可以改變服務(wù)器上的某些內(nèi)容的請(qǐng)求,例如創(chuàng)建一個(gè)新帳戶或發(fā)布消息,應(yīng)該用其他方法表示,例如POST。 諸如瀏覽器之類的客戶端軟件,知道它不應(yīng)該盲目地發(fā)出POST請(qǐng)求,但通常會(huì)隱式地發(fā)出GET請(qǐng)求 - 例如預(yù)先獲取一個(gè)它認(rèn)為用戶很快需要的資源。
我們將在本章后面的回到表單,以及如何與 JavaScript 交互。
Fetch瀏覽器 JavaScript 可以通過fetch接口生成 HTTP 請(qǐng)求。 由于它比較新,所以它很方便地使用了Promise(這在瀏覽器接口中很少見)。
fetch("example/data.txt").then(response => { console.log(response.status); // → 200 console.log(response.headers.get("Content-Type")); // → text/plain });
調(diào)用fetch返回一個(gè)Promise,它解析為一個(gè)Response對(duì)象,該對(duì)象包含服務(wù)器響應(yīng)的信息,例如狀態(tài)碼和協(xié)議頭。 協(xié)議頭被封裝在類Map的對(duì)象中,該對(duì)象不區(qū)分鍵(協(xié)議頭名稱)的大小寫,因?yàn)閰f(xié)議頭名稱不應(yīng)區(qū)分大小寫。 這意味著header.get("Content-Type")和headers.get("content-TYPE")將返回相同的值。
請(qǐng)注意,即使服務(wù)器使用錯(cuò)誤代碼進(jìn)行響應(yīng),由fetch返回的Promise也會(huì)成功解析。 如果存在網(wǎng)絡(luò)錯(cuò)誤或找不到請(qǐng)求的服務(wù)器,它也可能被拒絕。
fetch的第一個(gè)參數(shù)是請(qǐng)求的 URL。 當(dāng)該 URL 不以協(xié)議名稱(例如http:)開頭時(shí),它被視為相對(duì)路徑,這意味著它解釋為相對(duì)于當(dāng)前文檔的路徑。 當(dāng)它以斜線(/)開始時(shí),它將替換當(dāng)前路徑,即服務(wù)器名稱后面的部分。 否則,當(dāng)前路徑直到并包括最后一個(gè)斜杠的部分,放在相對(duì) URL 前面。
為了獲取響應(yīng)的實(shí)際內(nèi)容,可以使用其text方法。 由于初始Promise在收到響應(yīng)頭文件后立即解析,并且讀取響應(yīng)正文可能需要一段時(shí)間,這又會(huì)返回一個(gè)Promise。
fetch("example/data.txt") .then(resp => resp.text()) .then(text => console.log(text)); // → This is the content of data.txt
有一種類似的方法,名為json,它返回一個(gè)Promise,它將解析為,將正文解析為 JSON 時(shí)得到的值,或者不是有效的 JSON,則被拒絕。
默認(rèn)情況下,fetch使用GET方法發(fā)出請(qǐng)求,并且不包含請(qǐng)求正文。 你可以通過傳遞一個(gè)帶有額外選項(xiàng)的對(duì)象作為第二個(gè)參數(shù),來進(jìn)行不同的配置。 例如,這個(gè)請(qǐng)求試圖刪除example/data.txt。
fetch("example/data.txt", {method: "DELETE"}).then(resp => { console.log(resp.status); // → 405 });
405 狀態(tài)碼意味著“方法不允許”,這是 HTTP 服務(wù)器說“我不能這樣做”的方式。
為了添加一個(gè)請(qǐng)求正文,你可以包含body選項(xiàng)。 為了設(shè)置標(biāo)題,存在headers選項(xiàng)。 例如,這個(gè)請(qǐng)求包含Range協(xié)議,它指示服務(wù)器只返回一部分響應(yīng)。
fetch("example/data.txt", {headers: {Range: "bytes=8-19"}}) .then(resp => resp.text()) .then(console.log); // → the content
瀏覽器將自動(dòng)添加一些請(qǐng)求頭,例如Host和服務(wù)器需要的協(xié)議頭,來確定正文的大小。 但是對(duì)于包含認(rèn)證信息或告訴服務(wù)器想要接收的文件格式,添加自己的協(xié)議頭通常很有用。
HTTP 沙箱在網(wǎng)頁腳本中發(fā)出 HTTP 請(qǐng)求,再次引發(fā)了安全性的擔(dān)憂。 控制腳本的人的興趣可能不同于正在運(yùn)行的計(jì)算機(jī)的所有者。 更具體地說,如果我訪問themafia.org,我不希望其腳本能夠使用來自我的瀏覽器的身份向mybank.com發(fā)出請(qǐng)求,并且下令將我所有的錢轉(zhuǎn)移到某個(gè)隨機(jī)帳戶。
出于這個(gè)原因,瀏覽器通過禁止腳本向其他域(如themafia.org和mybank.com等名稱)發(fā)送 HTTP 請(qǐng)求來保護(hù)我們。
在構(gòu)建希望因合法原因訪問多個(gè)域的系統(tǒng)時(shí),這可能是一個(gè)惱人的問題。 幸運(yùn)的是,服務(wù)器可以在響應(yīng)中包含這樣的協(xié)議頭,來明確地向?yàn)g覽器表明,請(qǐng)求可以來自另一個(gè)域:
Access-Control-Allow-Origin: *運(yùn)用 HTTP
當(dāng)構(gòu)建一個(gè)需要讓瀏覽器(客戶端)的 JavaScript 程序和服務(wù)器端的程序進(jìn)行通信的系統(tǒng)時(shí),有一些不同的方式可以實(shí)現(xiàn)這個(gè)功能。
一個(gè)常用的方法是遠(yuǎn)程過程調(diào)用,通信遵從正常的方法調(diào)用方式,不過調(diào)用的方法實(shí)際運(yùn)行在另一臺(tái)機(jī)器中。調(diào)用包括向服務(wù)器發(fā)送包含方法名和參數(shù)的請(qǐng)求。響應(yīng)的結(jié)果則包括函數(shù)的返回值。
當(dāng)考慮遠(yuǎn)程過程調(diào)用時(shí),HTTP 只是通信的載體,并且你很可能會(huì)寫一個(gè)抽象層來隱藏細(xì)節(jié)。
另一個(gè)方法是使用一些資源和 HTTP 方法來建立自己的通信。不同于遠(yuǎn)程調(diào)用方法addUser,你需要發(fā)送一個(gè)PUT請(qǐng)求到users/larry,不同于將用戶屬性進(jìn)行編碼后作為參數(shù)傳遞,你定義了一個(gè) JSON 文檔格式(或使用一種已有的格式)來展示一個(gè)用戶。PUT請(qǐng)求的正文則只是這樣的一個(gè)用來建立新資源的文檔。由GET方法獲取的資源則是自愿的 URL(例如,/users/larry),該 URL 返回代表這個(gè)資源的文檔。
第二種方法使用了 HTTP 的一些特性,所以使得整體更簡(jiǎn)潔。例如對(duì)于資源緩存的支持(在客戶端存一份副本用于快速訪問)。HTTP 中使用的概念設(shè)計(jì)良好,可以提供一組有用的原則來設(shè)計(jì)服務(wù)器接口。
安全和 HTTPS通過互聯(lián)網(wǎng)傳播的數(shù)據(jù),往往走過漫長(zhǎng)而危險(xiǎn)的道路。 為了到達(dá)目的地,它必須跳過任何東西,從咖啡店的 Wi-Fi 到由各個(gè)公司和國(guó)家管理的網(wǎng)絡(luò)。 在它的路線上的任何位置,它都可能被探測(cè)或者甚至被修改。
如果對(duì)某件事保密是重要的,例如你的電子郵件帳戶的密碼,或者它到達(dá)目的地而未經(jīng)修改是重要的,例如帳戶號(hào)碼,你使用它在銀行網(wǎng)站上轉(zhuǎn)賬,純 HTTP 就不夠好了。
安全的 HTTP 協(xié)議,其 URL 以https://開頭,是一種難以閱讀和篡改的,HTTP 流量的封裝方式。 在交換數(shù)據(jù)之前,客戶端證實(shí)該服務(wù)器是它所聲稱的東西,通過要求它證明,它具有由瀏覽器承認(rèn)的證書機(jī)構(gòu)所頒發(fā)的證書。 接下來,通過連接傳輸?shù)乃袛?shù)據(jù),都將以某種方式加密,它應(yīng)該防止竊聽和篡改。
因此,當(dāng) HTTPS 正常工作時(shí),它可以阻止某人冒充你想要與之通話的網(wǎng)站,以及某人窺探你的通信。 這并不完美,由于偽造或被盜的證書和損壞的軟件,存在各種 HTTPS 失敗的事故,但它比純 HTTP 更安全。
表單字段表單最初是為 JavaScript 之前的網(wǎng)頁設(shè)計(jì)的,允許網(wǎng)站通過 HTTP 請(qǐng)求發(fā)送用戶提交的信息。 這種設(shè)計(jì)假定與服務(wù)器的交互,總是通過導(dǎo)航到新頁面實(shí)現(xiàn)。
但是它們的元素是 DOM 的一部分,就像頁面的其他部分一樣,并且表示表單字段的 DOM 元素,支持許多其他元素上不存在的屬性和事件。 這些使其可以使用 JavaScript 程序檢查和控制這些輸入字段,以及可以執(zhí)行一些操作,例如向表單添加新功能,或在 JavaScript 應(yīng)用程序中使用表單和字段作為積木。
一個(gè)網(wǎng)頁表單在其標(biāo)簽中包含若干個(gè)輸入字段。HTML 允許多個(gè)的不同風(fēng)格的輸入字段,從簡(jiǎn)單的開關(guān)選擇框到下拉菜單和進(jìn)行輸入的字段。本書不會(huì)全面的討論每一個(gè)輸入字段類型,不過我們會(huì)先大概講述一下。
很多字段類型都使用標(biāo)簽。標(biāo)簽的type屬性用來選擇字段的種類,下面是一些常用的類型。
text:一個(gè)單行的文本輸入框。
password:和text相同但隱藏了輸入內(nèi)容。
checkbox:一個(gè)復(fù)選框。
radio:一個(gè)多選擇字段中的一個(gè)單選框。
file:允許用戶從本機(jī)選擇文件上傳。
表單字段并不一定要出現(xiàn)在標(biāo)簽中。你可以把表單字段放置在一個(gè)頁面的任何地方。但這樣不帶表單的字段不能被提交(一個(gè)完整的表單才可以),當(dāng)需要和 JavaScript 進(jìn)行響應(yīng)時(shí),我們通常也不希望按常規(guī)的方式提交表單。
(text)
(password)
(checkbox)
(radio)
(file)
這些元素的 JavaScript 接口和元素類型不同。
多行文本輸入框有其自己的標(biāo)簽,這樣做是因?yàn)橥ㄟ^一個(gè)屬性來聲明一個(gè)多行初始值會(huì)十分奇怪。要求有一個(gè)相匹配的結(jié)束標(biāo)簽并使用標(biāo)簽之間的文本作為初始值,而不是使用value屬性存儲(chǔ)文本。
標(biāo)簽用來創(chuàng)造一個(gè)可以讓用戶從一些提前設(shè)定好的選項(xiàng)中進(jìn)行選擇的字段。
當(dāng)一個(gè)表單字段中的內(nèi)容更改時(shí)會(huì)觸發(fā)change事件。
聚焦不同于 HTML 文檔中的其他元素,表單字段可以獲取鍵盤焦點(diǎn)。當(dāng)點(diǎn)擊或以某種方式激活時(shí),他們會(huì)成為激活的元素,并接受鍵盤的輸入。
因此,只有獲得焦點(diǎn)時(shí),你才能輸入文本字段。 其他字段對(duì)鍵盤事件的響應(yīng)不同。 例如,菜單嘗試移動(dòng)到包含用戶輸入文本的選項(xiàng),并通過向上和向下移動(dòng)其選項(xiàng)來響應(yīng)箭頭鍵。
我們可以通過使用 JavaScript 的focus和blur方法來控制聚焦。第一個(gè)會(huì)聚焦到某一個(gè) DOM 元素,第二個(gè)則使其失焦。在document.activeElement中的值會(huì)關(guān)聯(lián)到當(dāng)前聚焦的元素。
對(duì)于一些頁面,用戶希望立刻使用到一個(gè)表單字段。JavaScript 可以在頁面載入完成時(shí)將焦點(diǎn)放到這些字段上,HTML 提供了autofocus屬性,可以實(shí)現(xiàn)相同的效果,并讓瀏覽器知道我們正在嘗試實(shí)現(xiàn)的事情。這向?yàn)g覽器提供了選項(xiàng),來禁用一些錯(cuò)誤的操作,例如用戶希望將焦點(diǎn)置于其他地方。
瀏覽器也允許用戶通過 TAB 鍵來切換焦點(diǎn)。通過tabindex屬性可以改變?cè)亟邮芙裹c(diǎn)的順序。后面的例子會(huì)讓焦點(diǎn)從文本輸入框跳轉(zhuǎn)到 OK 按鈕而不是到幫助鏈接。
(help)
默認(rèn)情況下,多數(shù)的 HTML 元素不能擁有焦點(diǎn)。但是可以通過添加tabindex屬性使任何元素可聚焦。tabindex為 -1 使 TAB 鍵跳過元素,即使它通常是可聚焦的。
禁用字段所有的表單字段都可以通過其disable屬性來禁用。它是一個(gè)可以被指定為沒有值的屬性 - 事實(shí)上它出現(xiàn)在所有禁用的元素中。
禁用的字段不能擁有焦點(diǎn)或更改,瀏覽器使它們變成灰色。
當(dāng)一個(gè)程序在處理一些由按鍵或其他控制方式出發(fā)的事件,并且這些事件可能要求和服務(wù)器的通信時(shí),將元素禁用直到動(dòng)作完成可能是一個(gè)很好的方法。按照這用方式,當(dāng)用戶失去耐心并且再次點(diǎn)擊時(shí),不會(huì)意外的重復(fù)這一動(dòng)作。
作為整體的表單當(dāng)一個(gè)字段被包含在元素中時(shí),其 DOM 元素會(huì)有一個(gè)form屬性指向form的 DOM 元素。元素則會(huì)有一個(gè)叫作elements屬性,包含一個(gè)類似于數(shù)據(jù)的集合,其中包含全部的字段。
一個(gè)表單字段的name屬性會(huì)決定在form提交時(shí)其內(nèi)容的辨別方式。同時(shí)在獲取form的elements屬性時(shí)也可以作為一種屬性名,所以elements屬性既可以像數(shù)組(由編號(hào)來訪問)一樣使用也可以像映射一樣訪問(通過名字訪問)。
type屬性為submit的按鈕在點(diǎn)擊時(shí),會(huì)提交表單。在一個(gè)form擁有焦點(diǎn)時(shí),點(diǎn)擊enter鍵也會(huì)有同樣的效果。
通常在提交一個(gè)表單時(shí),瀏覽器會(huì)將頁面導(dǎo)航到form的action屬性指明的頁面,使用GET或POST請(qǐng)求。但是在這些發(fā)生之前,"submit"事件會(huì)被觸發(fā)。這個(gè)事件可以由 JavaScript 處理,并且處理器可以通過調(diào)用事件對(duì)象的preventDefault來禁用默認(rèn)行為。
在 JavaScript 中submit事件有多種用途。我們可以編寫代碼來檢測(cè)用戶輸入是否正確并且立刻提示錯(cuò)誤信息,而不是提交表單?;蛘呶覀兛梢越谜5奶峤环绞?,正如這個(gè)例子中,讓我們的程序處理輸入,可能使用fetch將其發(fā)送到服務(wù)器而不重新加載頁面。
文本字段由type屬性為text或password的標(biāo)簽和textarea標(biāo)簽組成的字段有相同的接口。其 DOM 元素都有一個(gè)value屬性,保存了為字符串格式的當(dāng)前內(nèi)容。將這個(gè)屬性更改為另一個(gè)值將改變字段的內(nèi)容。
文本字段selectionStart和selectEnd屬性包含光標(biāo)和所選文字的信息。當(dāng)沒有選中文字時(shí),這兩個(gè)屬性的值相同,表明當(dāng)前光標(biāo)的信息。例如,0 表示文本的開始,10 表示光標(biāo)在第十個(gè)字符之后。當(dāng)一部分字段被選中時(shí),這兩個(gè)屬性值會(huì)不同,表明選中文字開始位置和結(jié)束位置。
和正常的值一樣,這些屬性也可以被更改。
想象你正在編寫關(guān)于 Knaseknemwy 的文章,但是名字拼寫有一些問題,后續(xù)代碼將標(biāo)簽和一個(gè)事件處理器關(guān)聯(lián)起來,當(dāng)點(diǎn)擊F2時(shí),插入 Knaseknemwy。
replaceSelection函數(shù)用給定的字符串替換當(dāng)前選中的文本字段內(nèi)容,并將光標(biāo)移動(dòng)到替換內(nèi)容后讓用戶可以繼續(xù)輸入。change事件不會(huì)在每次有輸入時(shí)都被調(diào)用,而是在內(nèi)容在改變并失焦后觸發(fā)。為了及時(shí)的響應(yīng)文本字段的改變,則需要為input事件注冊(cè)一個(gè)處理器,每當(dāng)用戶有輸入或更改時(shí)就被觸發(fā)。
下面的例子展示一個(gè)文本字段和一個(gè)展示字段中的文字的當(dāng)前長(zhǎng)度的計(jì)數(shù)器。
length: 0
選擇框和單選框
一個(gè)選擇框只是一個(gè)雙選切換。其值可以通過其包含一個(gè)布爾值的checked屬性來獲取和更改。
標(biāo)簽關(guān)聯(lián)部分文本和一個(gè)輸入字段。點(diǎn)擊標(biāo)簽上的任何位置將激活該字段,這樣會(huì)將其聚焦,并當(dāng)它為復(fù)選框或單選按鈕時(shí)切換它的值。
單選框和選擇框類似,不過單選框可以通過相同的name屬性,隱式關(guān)聯(lián)其他幾個(gè)單選框,保證只能選擇其中一個(gè)。
Color:
提供給querySelectorAll的 CSS 查詢中的方括號(hào)用于匹配屬性。 它選擇name屬性為"color"的元素。
選擇字段選擇字段和單選按鈕比較相似,允許用戶從多個(gè)選項(xiàng)中選擇。但是,單選框的展示排版是由我們控制的,而標(biāo)簽外觀則是由瀏覽器控制。
選擇字段也有一個(gè)更類似于復(fù)選框列表的變體,而不是單選框。 當(dāng)賦予multiple屬性時(shí),標(biāo)簽將允許用戶選擇任意數(shù)量的選項(xiàng),而不僅僅是一個(gè)選項(xiàng)。 在大多數(shù)瀏覽器中,這會(huì)顯示與正常的選擇字段不同的效果,后者通常顯示為下拉控件,僅在你打開它時(shí)才顯示選項(xiàng)。
每一個(gè)選項(xiàng)會(huì)有一個(gè)值,這個(gè)值可以通過value屬性來定義。如果沒有提供,選項(xiàng)內(nèi)的文本將作為其值。的value屬性反映了當(dāng)前的選中項(xiàng)。對(duì)于一個(gè)多選字段,這個(gè)屬性用處不太大因?yàn)樵搶傩灾粫?huì)給出一個(gè)選中項(xiàng)。
字段的標(biāo)簽可以通過一個(gè)類似于數(shù)組對(duì)象的options屬性訪問到。每個(gè)選項(xiàng)會(huì)有一個(gè)叫作selected的屬性,來表明這個(gè)選項(xiàng)當(dāng)前是否被選中。這個(gè)屬性可以用來被設(shè)定選中或不選中。
這個(gè)例子會(huì)從多選字段中取出選中的數(shù)值,并使用這些數(shù)值構(gòu)造一個(gè)二進(jìn)制數(shù)字。按住CTRL(或 Mac 的COMMAND鍵)來選擇多個(gè)選項(xiàng)。
= 0
文件字段
文件字段最初是用于通過表單來上傳從瀏覽器機(jī)器中獲取的文件。在現(xiàn)代瀏覽器中,也可以從 JavaScript 程序中讀取文件。該字段則作為一個(gè)看門人角色。腳本不能簡(jiǎn)單地直接從用戶的電腦中讀取文件,但是如果用戶在這個(gè)字段中選擇了一個(gè)文件,瀏覽器會(huì)將這個(gè)行為解釋為腳本,便可以訪問該文件。
一個(gè)文本字段是一個(gè)類似于“選擇文件”或“瀏覽”標(biāo)簽的按鈕,后面跟著所選文件的信息。
文本字段的files屬性是一個(gè)類數(shù)組對(duì)象(當(dāng)然,不是一個(gè)真正的數(shù)組),包含在字段中所選擇的文件。開始時(shí)是空的。因此文本字段屬性不僅僅是file屬性。有時(shí)文本字段可以上傳多個(gè)文件,這使得同時(shí)選擇多個(gè)文件變?yōu)榭赡堋?/p>
files對(duì)象中的對(duì)象有name(文件名)、size(文件大小,單位為字節(jié)),和type(文件的媒體類型,如text/plain,image/jpeg)等屬性。
而files屬性中不包含文件內(nèi)容的屬性。獲取這個(gè)內(nèi)容會(huì)比較復(fù)雜。由于從硬盤中讀取文件會(huì)需要一些時(shí)間,接口必須是異步的,來避免文檔的無響應(yīng)問題。
讀取文件是通過FileReader對(duì)象實(shí)現(xiàn)的,注冊(cè)一個(gè)load事件處理器,然后調(diào)用readAsText方法,傳入我們希望讀取的文件,一旦載入完成,reader的result屬性內(nèi)容就是文件內(nèi)容。
FileReader對(duì)象還會(huì)在讀取文件失敗時(shí)觸發(fā)error事件。錯(cuò)誤對(duì)象本身會(huì)存在reader的error屬性中。這個(gè)接口是在Promise成為語言的一部分之前設(shè)計(jì)的。 你可以把它包裝在Promise中,像這樣:
function readFileText(file) { return new Promise((resolve, reject) => { let reader = new FileReader(); reader.addEventListener( "load", () => resolve(reader.result)); reader.addEventListener( "error", () => reject(reader.error)); }); reader.readAsText(file); }); }客戶端保存數(shù)據(jù)
采用 JavaScript 代碼的簡(jiǎn)單 HTML 頁面可以作為實(shí)現(xiàn)一些小應(yīng)用的很好的途徑??梢圆捎眯〉膸椭绦騺碜詣?dòng)化一些基本的任務(wù)。通過關(guān)聯(lián)一些表單字段和事件處理器,你可以實(shí)現(xiàn)華氏度與攝氏度的轉(zhuǎn)換。也可以實(shí)現(xiàn)由主密碼和網(wǎng)站名來生成密碼等各種任務(wù)。
當(dāng)一個(gè)應(yīng)用需要存儲(chǔ)一些東西以便于跨對(duì)話使用時(shí),則不能使用 JavaScript 綁定因?yàn)槊慨?dāng)頁面關(guān)閉時(shí)這些值就會(huì)丟失。你可以搭建一個(gè)服務(wù)器,連接到因特網(wǎng),將一些服務(wù)數(shù)據(jù)存儲(chǔ)到其中。在第20章中將會(huì)介紹如何實(shí)現(xiàn)這些,當(dāng)然這需要很多的工作,也有一定的復(fù)雜度。有時(shí)只要將數(shù)據(jù)存儲(chǔ)在瀏覽器中即可。
localStorage對(duì)象可以用于保存數(shù)據(jù),它在頁面重新加載后還存在。這個(gè)對(duì)象允許你將字符串存儲(chǔ)在某個(gè)名字(也是字符串)下,下面是具體示例。
localStorage.setItem("username", "marijn"); console.log(localStorage.getItem("username")); // → marijn localStorage.removeItem("username");
一個(gè)在localStorage中的值會(huì)保留到其被重寫時(shí),它也可以通過removeItem來清除,或者由用戶清除本地?cái)?shù)據(jù)。
不同字段名的站點(diǎn)的數(shù)據(jù)會(huì)存在不同的地方。這也表明原則上由localStorage存儲(chǔ)的數(shù)據(jù)只可以由相同站點(diǎn)的腳本編輯。
瀏覽器的確限制一個(gè)站點(diǎn)可以存儲(chǔ)的localStorage的數(shù)據(jù)大小。這種限制,以及用垃圾填滿人們的硬盤并不是真正有利可圖的事實(shí),防止該特性占用太多空間。
下面的代碼實(shí)現(xiàn)了一個(gè)粗糙的筆記應(yīng)用。程序?qū)⒂脩舻墓P記保存為一個(gè)對(duì)象,將筆記的標(biāo)題和內(nèi)容字符串相關(guān)聯(lián)。對(duì)象被編碼為 JSON 格式并存儲(chǔ)在localStorage中。用戶可以從選擇字段中選擇筆記并在中編輯筆記,并可以通過點(diǎn)擊一個(gè)按鈕來添加筆記。
Notes:
腳本從存儲(chǔ)在localStorage中的"Notes"值來獲取它的初始狀態(tài),如果其中沒有值,它會(huì)創(chuàng)建示例狀態(tài),僅僅帶有一個(gè)購物列表。從localStorage中讀取不存在的字段會(huì)返回null。
setState方法確保 DOM 顯示給定的狀態(tài),并將新狀態(tài)存儲(chǔ)到localStorage。 事件處理器調(diào)用這個(gè)函數(shù)來移動(dòng)到一個(gè)新狀態(tài)。
在這個(gè)例子中使用Object.assign,是為了創(chuàng)建一個(gè)新的對(duì)象,它是舊的state.notes的一個(gè)克隆,但是添加或覆蓋了一個(gè)屬性。 Object.assign選取第一個(gè)參數(shù),向其添加所有更多參數(shù)的所有屬性。 因此,向它提供一個(gè)空對(duì)象會(huì)使它填充一個(gè)新對(duì)象。 第三個(gè)參數(shù)中的方括號(hào)表示法,用于創(chuàng)建名稱基于某個(gè)動(dòng)態(tài)值的屬性。
還有另一個(gè)和localStorage很相似的對(duì)象叫作sessionStorage。這兩個(gè)對(duì)象之間的區(qū)別在于sessionStorage的內(nèi)容會(huì)在每次會(huì)話結(jié)束時(shí)丟失,而對(duì)于多數(shù)瀏覽器來說,會(huì)話會(huì)在瀏覽器關(guān)閉時(shí)結(jié)束。
本章小結(jié)在本章中,我們討論了 HTTP 協(xié)議的工作原理。 客戶端發(fā)送一個(gè)請(qǐng)求,該請(qǐng)求包含一個(gè)方法(通常是GET)和一個(gè)標(biāo)識(shí)資源的路徑。 然后服務(wù)器決定如何處理請(qǐng)求,并用狀態(tài)碼和響應(yīng)正文進(jìn)行響應(yīng)。 請(qǐng)求和響應(yīng)都可能包含提供附加信息的協(xié)議頭。
瀏覽器 JavaScript 可以通過fetch接口生成 HTTP 請(qǐng)求。 像這樣生成請(qǐng)求:
fetch("/18_http.html").then(r => r.text()).then(text => { console.log(`The page starts with ${text.slice(0, 15)}`); });
瀏覽器生成GET請(qǐng)求來獲取顯示網(wǎng)頁所需的資源。 頁面也可能包含表單,這些表單允許在提交表單時(shí),用戶輸入的信息發(fā)送為新頁面的請(qǐng)求。
HTML可以表示多種表單字段,例如文本字段、選擇框、多選字段和文件選取。
這些字段可以用 JavaScript 進(jìn)行控制和讀取。內(nèi)容改變時(shí)會(huì)觸發(fā)change事件,文本有輸入時(shí)會(huì)觸發(fā)input事件,鍵盤獲得焦點(diǎn)時(shí)觸發(fā)鍵盤事件。 例如"value"(用于文本和選擇字段)或"checked"(用于復(fù)選框和單選按鈕)的屬性,用于讀取或設(shè)置字段的內(nèi)容。
當(dāng)一個(gè)表單被提交時(shí),會(huì)觸發(fā)其submit事件,JavaScript 處理器可以通過調(diào)用preventDefault來禁用默認(rèn)的提交事件。表單字段的元素不一定需要被包裝在標(biāo)簽中。
當(dāng)用戶在一個(gè)文件選擇字段中選擇了本機(jī)中的一個(gè)文件時(shí),可以用FileReader接口來在 JavaScript 中獲取文件內(nèi)容。
localStorage和sessionStorage對(duì)象可以用來保存頁面重載后依舊保留的信息。第一個(gè)會(huì)永久保留數(shù)據(jù)(直到用戶決定清除),第二個(gè)則會(huì)保存到瀏覽器關(guān)閉時(shí)。
習(xí)題 內(nèi)容協(xié)商HTTP 可以做的事情之一就是內(nèi)容協(xié)商。 Accept請(qǐng)求頭用于告訴服務(wù)器,客戶端想要獲得什么類型的文檔。 許多服務(wù)器忽略這個(gè)協(xié)議頭,但是當(dāng)一個(gè)服務(wù)器知道各種編碼資源的方式時(shí),它可以查看這個(gè)協(xié)議頭,并發(fā)送客戶端首選的格式。
URL eloquentjavascript.net/author配置為響應(yīng)明文,HTML 或 JSON,具體取決于客戶端要求的內(nèi)容。 這些格式由標(biāo)準(zhǔn)化的媒體類型"text/plain","text/html"和"application/json"標(biāo)識(shí)。
發(fā)送請(qǐng)求來獲取此資源的所有三種格式。 使用傳遞給fetch的options對(duì)象中的headers屬性,將名為Accept的協(xié)議頭設(shè)置為所需的媒體類型。
最后,請(qǐng)嘗試請(qǐng)求媒體類型"application/rainbows+unicorns",并查看產(chǎn)生的狀態(tài)碼。
// Your code here.JavaScript 工作臺(tái)
構(gòu)建一個(gè)接口,允許用戶輸入和運(yùn)行一段 JavaScript 代碼。
在字段旁邊放置一個(gè)按鈕,當(dāng)按下該按鈕時(shí),使用我們?cè)诘?10 章中看到的Function構(gòu)造器,將文本包裝到一個(gè)函數(shù)中并調(diào)用它。 將函數(shù)的返回值或其引發(fā)的任何錯(cuò)誤轉(zhuǎn)換為字符串,并將其顯示在文本字段下。
Conway 的生命游戲
Conway 的生命游戲是一個(gè)簡(jiǎn)單的在網(wǎng)格中模擬生命的游戲,每一個(gè)細(xì)胞都可以生存或滅亡。對(duì)于每一代(回合),都要遵循以下規(guī)則:
任何細(xì)胞,周圍有少于兩個(gè)或多于三個(gè)的活著的鄰居,都會(huì)死亡。
任意細(xì)胞,擁有兩個(gè)或三個(gè)的活著的鄰居,可以生存到下一代。
任何死去的細(xì)胞,周圍有三個(gè)活著的鄰居,可以再次復(fù)活。
任意一個(gè)相連的細(xì)胞都可以稱為鄰居,包括對(duì)角相連。
注意這些規(guī)則要立刻應(yīng)用于整個(gè)網(wǎng)格,而不是一次一個(gè)網(wǎng)格。這表明鄰居的數(shù)目由開始的一代決定,并且鄰居在每一代時(shí)發(fā)生的變化不應(yīng)該影響給定細(xì)胞新的狀態(tài)。
使用任何一個(gè)你認(rèn)為合適的數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)這個(gè)游戲。使用Math.random來隨機(jī)的生成開始狀態(tài)。將其展示為一個(gè)選擇框組成的網(wǎng)格和一個(gè)生成下一代的按鈕。當(dāng)用戶選中或取消選中一個(gè)選擇框時(shí),其變化應(yīng)該影響下一代的計(jì)算。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105045.html
摘要:來源編程精解中文第三版翻譯項(xiàng)目原文譯者飛龍協(xié)議自豪地采用谷歌翻譯部分參考了編程精解第版,這是一本關(guān)于指導(dǎo)電腦的書。在可控的范圍內(nèi)編寫程序是編程過程中首要解決的問題。我們可以用中文來描述這些指令將數(shù)字存儲(chǔ)在內(nèi)存地址中的位置。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 自豪地...
摘要:在本例中,使用屬性指定鏈接的目標(biāo),其中表示超文本鏈接。您應(yīng)該認(rèn)為和元數(shù)據(jù)隱式出現(xiàn)在示例中,即使它們沒有實(shí)際顯示在文本中。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:JavaScript and the Browser 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分參考了《JavaScript 編程精解(第 2 版)》 ...
摘要:在這樣的程序中,異步編程通常是有幫助的。最初是為了使異步編程簡(jiǎn)單方便而設(shè)計(jì)的。在年設(shè)計(jì)時(shí),人們已經(jīng)在瀏覽器中進(jìn)行基于回調(diào)的編程,所以該語言的社區(qū)用于異步編程風(fēng)格。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:Node.js 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分參考了《JavaScript 編程精解(第 2 版)...
摘要:事件與節(jié)點(diǎn)每個(gè)瀏覽器事件處理器被注冊(cè)在上下文中。事件對(duì)象雖然目前為止我們忽略了它,事件處理器函數(shù)作為對(duì)象傳遞事件對(duì)象。若事件處理器不希望執(zhí)行默認(rèn)行為通常是因?yàn)橐呀?jīng)處理了該事件,會(huì)調(diào)用事件對(duì)象的方法。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:Handling Events 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 自豪地采用谷歌翻譯 部分...
摘要:相反,當(dāng)響應(yīng)指針事件時(shí),它會(huì)調(diào)用創(chuàng)建它的代碼提供的回調(diào)函數(shù),該函數(shù)將處理應(yīng)用的特定部分?;卣{(diào)函數(shù)可能會(huì)返回另一個(gè)回調(diào)函數(shù),以便在按下按鈕并且將指針移動(dòng)到另一個(gè)像素時(shí)得到通知。它們?yōu)榻M件構(gòu)造器的數(shù)組而提供。 來源:ApacheCN『JavaScript 編程精解 中文第三版』翻譯項(xiàng)目原文:Project: A Pixel Art Editor 譯者:飛龍 協(xié)議:CC BY-NC-SA 4...
閱讀 2188·2023-04-25 19:06
閱讀 1388·2021-11-17 09:33
閱讀 1776·2019-08-30 15:53
閱讀 2599·2019-08-30 14:20
閱讀 3553·2019-08-29 12:58
閱讀 3552·2019-08-26 13:27
閱讀 512·2019-08-26 12:23
閱讀 493·2019-08-26 12:22