1
0
mirror of https://github.com/apachecn/eloquent-js-3e-zh.git synced 2025-05-23 20:02:20 +00:00
This commit is contained in:
wizardforcel 2018-05-14 11:47:32 +08:00
parent 8549a23f1a
commit 50f6978d6b

332
18.md
View File

@ -204,7 +204,11 @@ Access-Control-Allow-Origin: *
## 表单字段 ## 表单字段
一个网页表单在其<form>标签中包含若干个输入字段。HTML允许一定数量的不同风格的输入字段从简单的开关选择框到下拉菜单和进行输入的字段。本书不会全面的讨论每一个输入字段类型不过我们会先大概讲述一下。 表单最初是为 JavaScript 之前的网页设计的,允许网站通过 HTTP 请求发送用户提交的信息。 这种设计假定与服务器的交互,总是通过导航到新页面实现。
但是它们的元素是 DOM 的一部分,就像页面的其他部分一样,并且表示表单字段的 DOM 元素,支持许多其他元素上不存在的属性和事件。 这些使其可以使用 JavaScript 程序检查和控制这些输入字段,以及可以执行一些操作,例如向表单添加新功能,或在 JavaScript 应用程序中使用表单和字段作为积木。
一个网页表单在其<form>标签中包含若干个输入字段。HTML允许多个的不同风格的输入字段从简单的开关选择框到下拉菜单和进行输入的字段。本书不会全面的讨论每一个输入字段类型不过我们会先大概讲述一下。
很多字段类型都使用<input>标签。标签的type属性用来选择字段的种类下面是一些常用的<input>类型。 很多字段类型都使用<input>标签。标签的type属性用来选择字段的种类下面是一些常用的<input>类型。
@ -218,7 +222,7 @@ Access-Control-Allow-Origin: *
+ file允许用户从本机选择文件上传。 + file允许用户从本机选择文件上传。
表单字段并不一定要出现在<form>标签中。你可以把表单字段放置在一个页面的任何地方。但这样的字段不能被提交一个完整的表单才可以当需要和JavaScript进行响应时我们通常也不希望按常规的方式提交表单。 表单字段并不一定要出现在<form>标签中。你可以把表单字段放置在一个页面的任何地方。但这样不带表单的字段不能被提交一个完整的表单才可以当需要和JavaScript进行响应时我们通常也不希望按常规的方式提交表单。
```html ```html
<p><input type="text" value="abc"> (text)</p> <p><input type="text" value="abc"> (text)</p>
@ -230,7 +234,7 @@ Access-Control-Allow-Origin: *
<p><input type="file"> (file)</p> <p><input type="file"> (file)</p>
``` ```
JavaScript对于这些不同类型的元素有一些接口。本章后面会讲述这些接口 这些元素的 JavaScript 接口和元素类型不同
多行文本输入框有其自己的标签<textarea>,这样做是因为通过一个属性来声明一个多行初始值会十分奇怪。<textarea>要求有一个相匹配的</textarea>结束标签并使用标签之间的文本作为初始值而不是使用value属性存储文本。 多行文本输入框有其自己的标签<textarea>,这样做是因为通过一个属性来声明一个多行初始值会十分奇怪。<textarea>要求有一个相匹配的</textarea>结束标签并使用标签之间的文本作为初始值而不是使用value属性存储文本。
@ -254,11 +258,11 @@ three
当一个表单字段中的内容更改时会触发change事件。 当一个表单字段中的内容更改时会触发change事件。
## 18.2 聚焦 ## 聚焦
不同于HTML文档中的其他元素表单字段可以获取键盘焦点。当点击或以某种方式激活时他们会成为激活的元素并接受键盘的输入。 不同于HTML文档中的其他元素表单字段可以获取键盘焦点。当点击或以某种方式激活时他们会成为激活的元素并接受键盘的输入。
如果一个文档有文本字段,只有在该字段被聚焦时才能有文本输入进去。其他字段以不同的方式来响应键盘事件。例如,一个<select>菜单会尝试移动到和用户输入相匹配的内容的选项,并可以响应方向按键,将选择器上下移动 因此,只有获得焦点时,你才能输入文本字段。 其他字段对键盘事件的响应不同。 例如,`<select>`菜单尝试移动到包含用户输入文本的选项,并通过向上和向下移动其选项来响应箭头键
我们可以通过使用JavaScript的focus和blur方法来控制聚焦。第一个会聚焦到某一个DOM元素第二个则使其失焦。在document.activeElement中的值会关联到当前聚焦的元素。 我们可以通过使用JavaScript的focus和blur方法来控制聚焦。第一个会聚焦到某一个DOM元素第二个则使其失焦。在document.activeElement中的值会关联到当前聚焦的元素。
@ -274,11 +278,7 @@ three
</script> </script>
``` ```
对于一些页面用户希望立刻使用到一个表单字段。JavaScript可以在页面载入完成时将焦点放到这些字段上HTML提供了autofocus属性可以实现相同的效果并让浏览器知道我们正在尝试实现的事情。这可以让浏览器来禁用一些错误的操作例如用户希望将焦点置于其他地方。 对于一些页面用户希望立刻使用到一个表单字段。JavaScript可以在页面载入完成时将焦点放到这些字段上HTML提供了autofocus属性可以实现相同的效果并让浏览器知道我们正在尝试实现的事情。这向浏览器提供了选项来禁用一些错误的操作例如用户希望将焦点置于其他地方。
```html
<input type="text" autofocus>
```
浏览器也允许用户通过TAB键来切换焦点。通过tabindex属性可以改变元素接受焦点的顺序。后面的例子会让焦点从文本输入框之间跳转到OK按钮而不是到帮助链接。 浏览器也允许用户通过TAB键来切换焦点。通过tabindex属性可以改变元素接受焦点的顺序。后面的例子会让焦点从文本输入框之间跳转到OK按钮而不是到帮助链接。
@ -287,22 +287,22 @@ three
<button onclick="console.log('ok')" tabindex=2>OK</button> <button onclick="console.log('ok')" tabindex=2>OK</button>
``` ```
默认情况下多数的HTML元素不能被聚焦。但是可以通过添加tabindex属性使任何元素可以对焦 默认情况下多数的HTML元素不能被聚焦。但是可以通过添加tabindex属性使任何元素可聚焦。`tabindex`为 -1 使 TAB 键跳过元素,即使它通常是可聚焦的
## 18.3 禁用字段 ## 禁用字段
所有的表单字段都可以通过其disable属性来禁用disable也作为一个元素的文档对象的属性 所有的表单字段都可以通过其disable属性来禁用。它是一个可以被指定为没有值的属性 - 事实上它出现在所有禁用的元素中
```html ```html
<button>I'm all right</button> <button>I'm all right</button>
<button disabled>I'm out</button> <button disabled>I'm out</button>
``` ```
禁用的字段不能被聚焦或更改,不同于激活的字段,它们会变成灰色的 禁用的字段不能被聚焦或更改,浏览器使它们变成灰色
当一个程序在处理一些由按键或其他控制方式出发的事件,并且这些事件可能要求和服务器的通信时,将元素禁用直到动作完成可能是一个很好的方法。按照这用方式,当用户失去耐心并且再次点击时,不会意外的重复这一动作。 当一个程序在处理一些由按键或其他控制方式出发的事件,并且这些事件可能要求和服务器的通信时,将元素禁用直到动作完成可能是一个很好的方法。按照这用方式,当用户失去耐心并且再次点击时,不会意外的重复这一动作。
## 18.4 作为整体的表单 ## 作为整体的表单
当一个字段被包含在<form>元素中时其DOM元素会有一个form属性指向form的DOM元素。<form>元素则会有一个叫作elements属性包含一个类似于数据的集合其中包含全部的字段。 当一个字段被包含在<form>元素中时其DOM元素会有一个form属性指向form的DOM元素。<form>元素则会有一个叫作elements属性包含一个类似于数据的集合其中包含全部的字段。
@ -315,7 +315,7 @@ three
<button type="submit">Log in</button> <button type="submit">Log in</button>
</form> </form>
<script> <script>
var form = document.querySelector("form"); let form = document.querySelector("form");
console.log(form.elements[1].type); console.log(form.elements[1].type);
// → password // → password
console.log(form.elements.password.type); console.log(form.elements.password.type);
@ -335,17 +335,17 @@ three
<button type="submit">Save</button> <button type="submit">Save</button>
</form> </form>
<script> <script>
var form = document.querySelector("form"); let form = document.querySelector("form");
form.addEventListener("submit", function(event) { form.addEventListener("submit", event => {
console.log("Saving value", form.elements.value.value); console.log("Saving value", form.elements.value.value);
event.preventDefault(); event.preventDefault();
}); });
</script> </script>
``` ```
在JavaScript中submit事件有多种用途。我们可以编写代码来检测用户输入是否正确并且立刻做出错误信息提示当输入值错误时中止提交表单。或者我们可以禁用正常的提交方式正如在之前的例子中使用自定义的程序来处理请求使用XMLHttpRequest来将其发送到服务器且不重新载入页面。 在JavaScript中submit事件有多种用途。我们可以编写代码来检测用户输入是否正确并且立刻提示错误信息,而不是提交表单。或者我们可以禁用正常的提交方式,正如这个例子中,让我们的程序处理输入,可能使用`fetch`将其发送到服务器而不重新加载页面。
## 18.5 文本字段 ## 文本字段
由type属性为text或password的<input>标签和textarea标签组成的字段有相同的接口。其DOM元素都有一个value属性保存了为字符串格式的当前内容。将这个属性更改为另一个值将改变字段的内容。 由type属性为text或password的<input>标签和textarea标签组成的字段有相同的接口。其DOM元素都有一个value属性保存了为字符串格式的当前内容。将这个属性更改为另一个值将改变字段的内容。
@ -353,13 +353,13 @@ three
和正常的值一样,这些属性也可以被更改。 和正常的值一样,这些属性也可以被更改。
下面的例子中有一个关于Knaseknemwy的文章但是名字拼写有一些问题,后续代码将<textarea>标签和一个事件处理程序关联起来当点击F2时插入Knaseknemwy。 想象你正在编写关于Knaseknemwy的文章但是名字拼写有一些问题,后续代码将<textarea>标签和一个事件处理关联起来当点击F2时插入Knaseknemwy。
```html ```html
<textarea></textarea> <textarea></textarea>
<script> <script>
var textarea = document.querySelector("textarea"); let textarea = document.querySelector("textarea");
textarea.addEventListener("keydown", function(event) { textarea.addEventListener("keydown", event => {
// The key code for F2 happens to be 113 // The key code for F2 happens to be 113
if (event.keyCode == 113) { if (event.keyCode == 113) {
replaceSelection(textarea, "Khasekhemwy"); replaceSelection(textarea, "Khasekhemwy");
@ -367,91 +367,86 @@ three
} }
}); });
function replaceSelection(field, word) { function replaceSelection(field, word) {
var from = field.selectionStart, to = field.selectionEnd; let from = field.selectionStart, to = field.selectionEnd;
field.value = field.value.slice(0, from) + word + field.value = field.value.slice(0, from) + word +
field.value.slice(to); field.value.slice(to);
// Put the cursor after the word // Put the cursor after the word
field.selectionStart = field.selectionEnd = field.selectionStart = from + word.length;
from + word.length; field.selectionEnd = from + word.length;
} }
</script> </script>
``` ```
replaceSelection函数用给定的字符串替换当前选中的文本字段内容并将光标移动到替换内容后让用户可以继续输入。Change事件不会在每次有输入时都被调用而是在内容在改变并失焦后触发。为了及时的响应文本字段的改变则需要为input事件注册一个处理函数每当用户有输入或更改时就被触发。 replaceSelection函数用给定的字符串替换当前选中的文本字段内容并将光标移动到替换内容后让用户可以继续输入。Change事件不会在每次有输入时都被调用而是在内容在改变并失焦后触发。为了及时的响应文本字段的改变则需要为input事件注册一个处理函数每当用户有输入或更改时就被触发。
下面的例子展示一个文本字段和一个展示当前输入文字长度的计数器。 下面的例子展示一个文本字段和一个展示字段中的文字的当前长度的计数器。
```html ```html
<input type="text"> length: <span id="length">0</span> <input type="text"> length: <span id="length">0</span>
<script> <script>
var text = document.querySelector("input"); let text = document.querySelector("input");
var output = document.querySelector("#length"); let output = document.querySelector("#length");
text.addEventListener("input", function() { text.addEventListener("input", () => {
output.textContent = text.value.length; output.textContent = text.value.length;
}); });
</script> </script>
``` ```
## 18.6 选择框和单选框 ## 选择框和单选框
一个选择框只是一个简单的双选切换。其值可以通过其包含一个布尔值的checked属性来获取和更改。 一个选择框只是一个双选切换。其值可以通过其包含一个布尔值的checked属性来获取和更改。
```html ```html
<input type="checkbox" id="purple"> <label>
<label for="purple">Make this page purple</label> <input type="checkbox" id="purple"> Make this page purple
</label>
<script> <script>
var checkbox = document.querySelector("#purple"); let checkbox = document.querySelector("#purple");
checkbox.addEventListener("change", function() { checkbox.addEventListener("change", () => {
document.body.style.background = document.body.style.background =
checkbox.checked ? "mediumpurple" : ""; checkbox.checked ? "mediumpurple" : "";
}); });
</script> </script>
``` ```
<label>标签用来将部分文本和一个输入字段关联。其for属性应该关联到一个字段的id属性。点击label会激活相关联的字段当字段为选择框或单选框时会聚焦到选择或单选框并切换其值。 <label>标签关联部分文本和一个输入字段。点击标签上的任何位置将激活该字段,这样会将其聚焦,并当它为复选框或单选按钮时切换它的值。
单选框和选择框类似不过单选框可以通过相同的name属性隐式的和其他几个单选框关联保证只能选择其中一个。 单选框和选择框类似不过单选框可以通过相同的name属性隐式的和其他几个单选框关联保证只能选择其中一个。
```html ```html
Color: Color:
<input type="radio" name="color" value="mediumpurple"> Purple <label>
<input type="radio" name="color" value="lightgreen"> Green <input type="radio" name="color" value="orange"> Orange
<input type="radio" name="color" value="lightblue"> Blue </label>
<label>
<input type="radio" name="color" value="lightgreen"> Green
</label>
<label>
<input type="radio" name="color" value="lightblue"> Blue
</label>
<script> <script>
var buttons = document.getElementsByName("color"); let buttons = document.querySelectorAll("[name=color]");
function setColor(event) { for (let button of Array.from(buttons)) {
document.body.style.background = event.target.value; button.addEventListener("change", () => {
document.body.style.background = button.value;
});
} }
for (var i = 0; i < buttons.length; i++)
buttons[i].addEventListener("change", setColor);
</script> </script>
``` ```
Document.getElementsByName方法可以返回全部name属性为给定值的元素。上例中循环遍历这些元素通过正常的for循环而不是forEach因为返回的集合不是一个真正的数据并且为每一个元素添加事件处理。事件对象有一个target属性关联到触发该事件的元素。在这样的事件处理中这个方法非常有用不同的元素会调用这个事件处理然后使用相同的方式访问到当前的目标 提供给`querySelectorAll`的 CSS 查询中的方括号用于匹配属性。 它选择`name`属性为`"color"`的元素
## 18.7 选择字段 ## 选择字段
选择字段和单选按钮比较相似,允许用户从多个选项中选择。但是,单选框的展示排版是由我们控制的,而<select>标签外观则是由浏览器控制。 选择字段和单选按钮比较相似,允许用户从多个选项中选择。但是,单选框的展示排版是由我们控制的,而<select>标签外观则是由浏览器控制。
选择字段的变体会和一系列的选择框很相似但不同于单选框的是当给出multiple属性时<select>标签会允许用户选择任意数量的选项而不是只有一个 选择字段也有一个更类似于复选框列表的变体,而不是单选框。 当赋予`multiple`属性时,`<select>`标签将允许用户选择任意数量的选项,而不仅仅是一个选项。 在大多数浏览器中,这会显示与正常的选择字段不同的效果,后者通常显示为下拉控件,仅在您打开它时才显示选项
```html 每一个<option>选项会有一个值这个值可以通过value属性来定义。如果没有提供选项内的文本将作为其值。<select>的value属性反映了当前的选中项。对于一个多选字段这个属性用处不太大因为该属性只会给出一个选中项。
<select multiple>
<option>Pancakes</option>
<option>Pudding</option>
<option>Ice cream</option>
</select>
```
在多数浏览器中,现实样式会和单选选择字段有所区别,单选选择字段有一个下拉控制按钮且只会在点击时打开。
<select>标签的size属性用来设置同时可展示的选项可以使得你对下拉菜单的外观进行控制。例如设置size属性为3会让这个字段显示三行不论是否有multiple选项。
每一个<option>选项会有一个值这个值可以通过value属性来定义。<select>的value属性反映了当前的选中项。对于一个多选字段这个属性用处不太大因为该属性只会给出一个选中项。
<select>字段的<option>标签可以通过一个类似于数组对象的options属性访问到。每个选项会有一个叫作selected的属性来表明这个选项当前是否被选中。这个属性可以用来被设定选中或不选中。 <select>字段的<option>标签可以通过一个类似于数组对象的options属性访问到。每个选项会有一个叫作selected的属性来表明这个选项当前是否被选中。这个属性可以用来被设定选中或不选中。
下面的例子会从多选字段中取出选中的数值并使用这些数值构造一个二进制数字。按住CTRL或Mac的COMMAND键来选择多个选项。 这个例子会从多选字段中取出选中的数值并使用这些数值构造一个二进制数字。按住CTRL或Mac的COMMAND键来选择多个选项。
```html ```html
<select multiple> <select multiple>
@ -461,21 +456,21 @@ Document.getElementsByName方法可以返回全部name属性为给定值的元
<option value="8">1000</option> <option value="8">1000</option>
</select> = <span id="output">0</span> </select> = <span id="output">0</span>
<script> <script>
var select = document.querySelector("select"); let select = document.querySelector("select");
var output = document.querySelector("#output"); let output = document.querySelector("#output");
select.addEventListener("change", function() { select.addEventListener("change", () => {
var number = 0; let number = 0;
for (var i = 0; i < select.options.length; i++) { for (let option of Array.from(select.options)) {
var option = select.options[i]; if (option.selected) {
if (option.selected)
number += Number(option.value); number += Number(option.value);
} }
}
output.textContent = number; output.textContent = number;
}); });
</script> </script>
``` ```
## 18.8 文件字段 ## 文件字段
文件字段最初是用于通过表单来上传从浏览器机器中获取的文件。在现代浏览器中也可以从JavaScript程序中读取文件。该字段则作为一个看门人角色。脚本不能简单地直接从用户的电脑中读取文件但是如果用户在这个字段中选择了一个文件浏览器会将这个行为解释为脚本便可以访问该文件。 文件字段最初是用于通过表单来上传从浏览器机器中获取的文件。在现代浏览器中也可以从JavaScript程序中读取文件。该字段则作为一个看门人角色。脚本不能简单地直接从用户的电脑中读取文件但是如果用户在这个字段中选择了一个文件浏览器会将这个行为解释为脚本便可以访问该文件。
@ -484,13 +479,12 @@ Document.getElementsByName方法可以返回全部name属性为给定值的元
```html ```html
<input type="file"> <input type="file">
<script> <script>
var input = document.querySelector("input"); let input = document.querySelector("input");
input.addEventListener("change", function() { input.addEventListener("change", () => {
if (input.files.length > 0) { if (input.files.length > 0) {
var file = input.files[0]; let file = input.files[0];
console.log("You chose", file.name); console.log("You chose", file.name);
if (file.type) if (file.type) console.log("It has type", file.type);
console.log("It has type", file.type);
} }
}); });
</script> </script>
@ -498,57 +492,52 @@ Document.getElementsByName方法可以返回全部name属性为给定值的元
文本字段的files属性是一个类似数组的对象当然不是一个真正的数组包含在字段中所选择的文件。开始时是空的。因此文本字段属性不仅仅是file属性。有时文本字段可以上传多个文件这使得同时选择多个文件变为可能。 文本字段的files属性是一个类似数组的对象当然不是一个真正的数组包含在字段中所选择的文件。开始时是空的。因此文本字段属性不仅仅是file属性。有时文本字段可以上传多个文件这使得同时选择多个文件变为可能。
files属性中的对象有name文件名、size文件大小单位为字节和type文件的媒体类型如text/plainimage/jped等属性。 files对象中的对象有name文件名、size文件大小单位为字节和type文件的媒体类型如text/plainimage/jped等属性。
而files属性中不包含文件内容的属性。获取这个内容会比较复杂。由于从硬盘中读取文件会需要一些时间接口需要使用异步方法来放置文档的无响应问题。你可以将FileReader构造器看作XMLHttpRequest但是FileReader是用于文件的 而files属性中不包含文件内容的属性。获取这个内容会比较复杂。由于从硬盘中读取文件会需要一些时间接口必须是异步的,来避免文档的无响应问题
```html ```html
<input type="file" multiple> <input type="file" multiple>
<script> <script>
var input = document.querySelector("input"); let input = document.querySelector("input");
input.addEventListener("change", function() { input.addEventListener("change", () => {
Array.prototype.forEach.call(input.files, function(file) { for (let file of Array.from(input.files)) {
var reader = new FileReader(); let reader = new FileReader();
reader.addEventListener("load", function() { reader.addEventListener("load", () => {
console.log("File", file.name, "starts with", console.log("File", file.name, "starts with",
reader.result.slice(0, 20)); reader.result.slice(0, 20));
}); });
reader.readAsText(file); reader.readAsText(file);
}); }
}); });
</script> </script>
``` ```
读取文件是通过FileReader对象实现的注册一个load事件处理程序然后调用readAsText方法传入我们希望读取的文件一旦载入完成reader的result属性内容就是文件内容。 读取文件是通过FileReader对象实现的注册一个load事件处理然后调用readAsText方法传入我们希望读取的文件一旦载入完成reader的result属性内容就是文件内容。
下面的例子中使用Array.prototype.forEach来迭代遍历数组因为通过正常的循环来从事件处理函数中获取正确的文件和reader对象会很不方便。变量可以被循环的全部迭代器获取到。 FileReader对象还会在读取文件失败时触发error事件。错误对象本身会存在reader的error属性中。这个接口是在`Promise`成为语言的一部分之前设计的。 你可以把它包装在`Promise`中,像这样:
FileReader对象还会在读取文件失败时触发error事件。错误对象本身会存在reader的error属性中。如果你不想去记住另一个奇怪的异步接口的细节你可以将其封装在Promise见17章
```html ```html
function readFile(file) { function readFileText(file) {
return new Promise(function(succeed, fail) { return new Promise((resolve, reject) => {
var reader = new FileReader(); let reader = new FileReader();
reader.addEventListener("load", function() { reader.addEventListener(
succeed(reader.result); "load", () => resolve(reader.result));
}); reader.addEventListener(
reader.addEventListener("error", function() { "error", () => reject(reader.error));
fail(reader.error);
}); });
reader.readAsText(file); reader.readAsText(file);
}); });
} }
``` ```
可以通过调用slice方法来获取文件的一部分并将结果一个叫作blob的对象传递给文件读取器。 ## 客户端保存数据
## 18.9 客户端保存数据 采用JavaScript代码的简单HTML页面可以作为实现一些小应用的很好的途径。可以采用小的帮助程序来自动化一些基本的任务。通过关联一些表单字段和事件处理函数你可以实现华氏度与摄氏度的转换。也可以实现由主密码和网站名来生成密码等各种任务。
采用JavaScript代码的简单HTML页面可以作为实现一些小应用的很好的途径。可以采用小的帮助程序来自动化处理一些日常事务。通过关联一些表单字段和事件处理函数你可以实现华氏度与摄氏度的转换。也可以实现由主密码和网站名来生成密码等各种任务 当一个应用需要存储一些东西以便于跨对话使用时则不能使用JavaScript绑定因为每当页面关闭时这些值就会丢失。你可以搭建一个服务器连接到因特网将一些服务数据存储到其中。在第20章中将会介绍如何实现这些当然这需要很多的工作也有一定的复杂度。有时只要将数据存储在浏览器中即可
当一个应用需要存储一些东西以便于跨对话使用时则不能使用JavaScript变量因为每当页面关闭时这些值就会丢失。你可以搭建一个服务器连接到因特网将一些服务数据存储到其中。在第20章中将会介绍如何实现这些当然这需要很多的工作也有一定的复杂度。有时只要将数据存储在浏览器中即可但是如何实现呢 `localStorage`对象可以用于保存数据,它在页面重新加载后还存在。这个对象允许你将字符串存储在某个名字(也是字符串)下,下面是具体示例。
你可以将字符串数据存储在localStorage对象中使得其可以在页面重新载入时继续保留。这个对象允许你将字符串存储在某个名字也是字符串下面是具体示例。
```js ```js
localStorage.setItem("username", "marijn"); localStorage.setItem("username", "marijn");
@ -561,75 +550,85 @@ localStorage.removeItem("username");
不同字段名的站点的数据会存在不同的地方。这也表明原则上由localStorage存储的数据只可以由相同站点的脚本编辑。 不同字段名的站点的数据会存在不同的地方。这也表明原则上由localStorage存储的数据只可以由相同站点的脚本编辑。
浏览器也会限制一个站点可以存储的localStorage的数据的大小通常只有几兆。这个可以限制用户的硬盘被垃圾文件填满防止了过多占用用户空间。 浏览器的确限制一个站点可以存储的localStorage的数据大小。这种限制以及用垃圾填满人们的硬盘并不是真正有利可图的事实防止该特性占用太多空间。
下面的代码实现了一个简单的记笔记的应用。程序将用户的笔记保存为一个对象将笔记的标题和内容字符串相关联。对象被编码为JSON格式并存储在localStorage中。用户可以从<select>选择字段中选择笔记并在<textarea>中编辑笔记,并可以通过点击一个按钮来添加笔记。 下面的代码实现了一个粗糙的笔记应用。程序将用户的笔记保存为一个对象将笔记的标题和内容字符串相关联。对象被编码为JSON格式并存储在localStorage中。用户可以从<select>选择字段中选择笔记并在<textarea>中编辑笔记,并可以通过点击一个按钮来添加笔记。
```html ```html
Notes: <select id="list"></select> Notes: <select></select> <button>Add</button><br>
<button onclick="addNote()">new</button><br> <textarea style="width: 100%"></textarea>
<textarea id="currentnote" style="width: 100%; height: 10em">
</textarea>
<script> <script>
var list = document.querySelector("#list"); let list = document.querySelector("select");
function addToList(name) { let note = document.querySelector("textarea");
var option = document.createElement("option");
let state;
function setState(newState) {
list.textContent = "";
for (let name of Object.keys(newState.notes)) {
let option = document.createElement("option");
option.textContent = name; option.textContent = name;
if (newState.selected == name) option.selected = true;
list.appendChild(option); list.appendChild(option);
} }
note.value = newState.notes[newState.selected];
// Initialize the list from localStorage localStorage.setItem("Notes", JSON.stringify(newState));
var notes = JSON.parse(localStorage.getItem("notes")) || state = newState;
{"shopping list": ""}; }
for (var name in notes) setState(JSON.parse(localStorage.getItem("Notes")) || {
if (notes.hasOwnProperty(name)) notes: {"shopping list": "Carrots\nRaisins"},
addToList(name); selected: "shopping list"
});
function saveToStorage() {
localStorage.setItem("notes", JSON.stringify(notes));
} }
var current = document.querySelector("#currentnote"); list.addEventListener("change", () => {
current.value = notes[list.value]; setState({notes: state.notes, selected: list.value});
});
list.addEventListener("change", function() { note.addEventListener("change", () => {
current.value = notes[list.value]; setState({
notes: Object.assign({}, state.notes,
{[state.selected]: note.value}),
selected: state.selected
}); });
current.addEventListener("change", function() {
notes[list.value] = current.value;
saveToStorage();
}); });
function addNote() { document.querySelector("button")
var name = prompt("Note name", ""); .addEventListener("click", () => {
if (!name) return; let name = prompt("Note name");
if (!notes.hasOwnProperty(name)) { if (name) setState({
notes[name] = ""; notes: Object.assign({}, state.notes, {[name]: ""}),
addToList(name); selected: name
saveToStorage(); });
} });
list.value = name;
current.value = notes[name];
}
</script> </script>
``` ```
脚本通过存储在localStorage中的值来初始化notes如果其中没有值则会使用一个简单的只有一个购物单的笔记对象来初始化。从localStorage中读取不存在的字段会返回null。 脚本从存储在localStorage中的`"Notes"`值来获取它的初始状态,如果其中没有值,它会创建示例状态,仅仅带有一个购物列表。从localStorage中读取不存在的字段会返回null。
将null值传递给JSON.parse会使得该方法对字符串null进行解析并返回null。因此对于这种情况||运算符可以用来提供一个默认值 `setState`方法确保 DOM 显示给定的状态,并将新状态存储到`localStorage`。 事件处理器调用这个函数来移动到一个新状态
当笔记数据更改时新的笔记添加或已有笔记更改时saveToStorage函数会被调用来更新存储字段。如果这个应用想要处理上千个笔记而不是几个的话这种做法的代价将会过大我们则需要用更复杂的方式来存储例如给每个笔记独有的存储空间。 在这个例子中使用`Object.assign`,是为了创建一个新的对象,它是旧的`state.notes`的一个克隆,但是添加或覆盖了一个属性。 `Object.assign`选取第一个参数,向其添加所有更多参数的所有属性。 因此,向它提供一个空对象会使它填充一个新对象。 第三个参数中的方括号表示法,用于创建名称基于某个动态值的属性。
当用户添加一个新的笔记时,代码必须明确的更新文本字段,尽管<select>字段有一个change处理函数进行同样的事情但代码实现这个功能依旧是必须的因为change事件只有在用户更改字段的数据时才会触发而不是脚本更改时。
还有另一个和localStorage很相似的对象叫作sessionStorage。这两个对象之间的区别在于sessionStorage的内容会在每次会话结束时丢失而对于多数浏览器来说会话会在浏览器关闭时结束。 还有另一个和localStorage很相似的对象叫作sessionStorage。这两个对象之间的区别在于sessionStorage的内容会在每次会话结束时丢失而对于多数浏览器来说会话会在浏览器关闭时结束。
## 18.10 本章小结 ## 本章小结
在本章中,我们讨论了 HTTP 协议的工作原理。 客户端发送一个请求,该请求包含一个方法(通常是`GET`)和一个标识资源的路径。 然后服务器决定如何处理请求,并用状态码和响应正文进行响应。 请求和响应都可能包含提供附加信息的协议头。
浏览器 JavaScript 可以通过`fetch`接口生成 HTTP 请求。 像这样生成请求:
```js
fetch("/18_http.html").then(r => r.text()).then(text => {
console.log(`The page starts with ${text.slice(0, 15)}`);
});
```
浏览器生成`GET`请求来获取显示网页所需的资源。 页面也可能包含表单,这些表单允许在提交表单时,用户输入的信息发送为新页面的请求。
HTML可以表示多种表单字段例如文本字段、选择框、多选字段和文件选取。 HTML可以表示多种表单字段例如文本字段、选择框、多选字段和文件选取。
这些字段可以用JavaScript进行控制和读取。当内容改变时会触发change事件当文本有输入时会触发input时间也还有多种键盘事件。这些事件允许我们获知用户与这些字段产生交互的时间。像value这样的属性对于文本和选择字段或checked属性对于选择框和单选框是用来读取或设置字段的值。 这些字段可以用 JavaScript 进行控制和读取。内容改变时会触发change事件文本有输入时会触发input事件键盘获得焦点时触发键盘事件。 例如“value”用于文本和选择字段或“checked”用于复选框和单选按钮的属性用于读取或设置字段的内容
当一个表单被提交时会触发其submit事件JavaScript处理器可以通过调用preventDefault来禁用默认的提交事件。表单字段的元素不一定需要被包装在<form>标签中。 当一个表单被提交时会触发其submit事件JavaScript处理器可以通过调用preventDefault来禁用默认的提交事件。表单字段的元素不一定需要被包装在<form>标签中。
@ -637,13 +636,27 @@ HTML可以表示多种表单字段例如文本字段、选择框、多选字
localStorage和sessionStorage对象可以用来保存页面重载后依旧保留的信息。第一个会永久保留数据直到用户决定清楚第二个则会保存到浏览器关闭时。 localStorage和sessionStorage对象可以用来保存页面重载后依旧保留的信息。第一个会永久保留数据直到用户决定清楚第二个则会保存到浏览器关闭时。
## 18.11 习题 ## 习题
### 18.11.1 一个JavaScript工作台 ### 内容协商
构建一个接口用于让用户输入并运行一段JavaScript代码 HTTP 可以做的事情之一就是内容协商。 `Accept`请求头用于告诉服务器,客户端想要获得什么类型的文档。 许多服务器忽略这个协议头,但是当一个服务器知道各种编码资源的方式时,它可以查看这个协议头,并发送客户端首选的格式
在一个<textarea>后面放置一个按钮当点击按钮时使用第10章中讲述过的Function构造器来将这些文本封装到一个函数中并调用。将函数运行结果或错误信息转换为字符串显示在<textarea>后面。 URL `eloquentjavascript.net/author`配置为响应明文HTML 或 JSON具体取决于客户端要求的内容。 这些格式由标准化的媒体类型“text / plain”“text / html”和“application / json”标识。
发送请求来获取此资源的所有三种格式。 使用传递给`fetch``options`对象中的`headers`属性,将名为`Accept`的协议头设置为所需的媒体类型。
最后请尝试请求媒体类型“application/rainbows+unicorns”并查看产生的状态码。
```js
// Your code here.
```
### JavaScript 工作台
构建一个接口,允许用户输入和运行一段 JavaScript 代码。
`<textarea>`字段旁边放置一个按钮,当按下该按钮时,使用我们在第 10 章中看到的`Function`构造器,将文本包装到一个函数中并调用它。 将函数的返回值或其引发的任何错误转换为字符串,并将其显示在文本字段下。
```html ```html
<textarea id="code">return "hi";</textarea> <textarea id="code">return "hi";</textarea>
@ -655,26 +668,7 @@ localStorage和sessionStorage对象可以用来保存页面重载后依旧保留
</script> </script>
``` ```
### 18.11.2 自动补全 ### Conway 的生命游戏
扩展一个文本字段,当用户输入时,一个建议值的列表显示在这个字段下面。提供一个可能的输入值数组,并展示出以当前输入的文本开头的值。当点击一个建议项时,将当前文本中的值用该值替换。
```html
<input type="text" id="field">
<div id="suggestions" style="cursor: pointer"></div>
<script>
// Builds up an array with global variable names, like
// 'alert', 'document', and 'scrollTo'
var terms = [];
for (var name in window)
terms.push(name);
// Your code here.
</script>
```
### 18.11.3 Conway的生命游戏
Conway的生命游戏是一个简单的在方格中模拟生命的游戏每一个小格都可以生存或灭亡。对于每一代生命都要遵循以下条件 Conway的生命游戏是一个简单的在方格中模拟生命的游戏每一个小格都可以生存或灭亡。对于每一代生命都要遵循以下条件