阿里云服务器非常实惠,而且很热门。 今年比今年更实惠。 10.24至11.11的认购价格为一年86元,三年229元。 您可以点击阅读原文参与。
为了保证可读性,本文采用音译而不是音译。
重新引入 HTML 表单
网页不仅仅用于显示数据。 通过 HTML 表单,我们可以收集和操作用户数据。 在本章中,您将通过创建简单的 HTML 表单来了解表单。
在此过程中,您将了解有关 DOM 事件的更多信息。 从第8章我们了解到元素是一个HTML元素,它可能包含其他子元素,例如:
在本章中,我们创建一个包含 、 和 的奖励。 理想情况下,每个输入都应该有一个 type 属性,它指示输入类型:例如文本、电子邮件、数字、日期等。除了 type 属性之外,您可能还想为每个表单元素添加一个 id 属性。
输入和文本区域也可以有名称属性。 如果您想在不使用 JS 的情况下发送表单,则 name 属性非常重要。 稍后会详细介绍这一点。
此外,它也是将每个表单元素关联起来的通用表单。 在下面的示例中,您将听到每个标签和 for 属性都绑定到相应输入元素的 id。 作用是点击标签元素聚焦输入。
如果未填写所有必填信息,用户将难以提交表格。 这是一个简单的验证,旨在防止出现空数据,从而防止用户跳过重要的数组。 有了这些知识,您现在就可以创建 HTML 表单了。 创建一个名为 form.html 的新文件并构建 HTML:
如上所述,表单中的输入具有正确的属性,从现在开始,可以通过填写一些数据来测试表单。 编写 HTML 表单时,请特别注意 type 属性,因为它决定用户可以输入哪些其他数据。
HTML5 还引入了表单验证:例如,电子邮件类型的输入仅接受带有“at”符号@的电子邮件地址。 不幸的是,这是应用于电子邮件地址的唯一检测:没有人会阻止用户输入像 a@a 这样的电子邮件。 它有@,但仍然无效(电子邮件输入的模式属性可以帮助解决这个问题。
有很多可用的属性,我发现 minlength 和 maxlength 是最有用的两个。 在实践中,他们可以阻止懒惰的垃圾邮件发送者发送带有“aa”或“testtest”的表单。
有了这个表格,我们就可以更进一步。 接下来,我们来看看这个表单是如何工作的。
表格如何运作
HTML 表单是 HTMLFormElement 类型的元素。 与几乎所有 HTML 元素一样,它连接到 HTMLElement,而 HTMLElement 又连接到 EventTarget。 当我们访问 DOM 元素时,它们被表示为 JS 对象。 在浏览器中尝试以下操作:
const aForm = document.createElement("form");
console.log(typeof aForm);
输出是一个“对象”,而像 HTMLElement 或 EventTarget 这样的实体是函数:
console.log(typeof EventTarget); // "function"
因此,如果任何 HTML 元素连接到 EventTarget,则意味着它是 EventTarget 的“实例”,如下所示:
const aForm = document.createElement("form");
console.log(aForm instanceof EventTarget); // true
form 是 EventTarget 的一种特殊类型。 每个EventTarget都可以接收并响应DOM事件(如第8章所示)。
DOM 事件有很多种类型,例如单击、模糊、更改等。现在,我们感兴趣的是 HTML 表单特有的提交事件。 当用户单击输入或“提交”类型的按钮(该元素必须存在于表单中)时,将调度提交事件,如下所示:
请注意,“提交”位于表单内。 一些开发者使用的输入法:
<input type="submit">
<button>提交表单</button>
<input type='image' src='av.gif'/>
只要表单中列出的任意键存在,那么当相应的表单控件获得焦点时,就可以通过按 Enter 键提交表单。 (Textarea 是一个例外,输入文本会导致换行。)如果表单中没有提交按钮,按 Enter 键将不会提交表单。
我们的目标是获取表单上的所有用户输入,因此我们需要监听提交事件。
const formSelector = document.querySelector("form");
new Form(formSelector);
DOM 还提供 document.forms,它是页面内所有表单的集合。 我们现在需要的是:
const formSelector = document.forms[0];
new Form(formSelector);
目前的看法是:给定一个表单选择器,我们可以注册一个storm监听器来响应表单的发送。 要注册侦听器html表单怎么做,我们可以使用构造函数并让它调用名为 init 的方法。 在 form.html 所在的文件夹中创建一个名为 form.js 的新文件,并从一个简单的类开始:
我们的风暴监听器是 this.handleSubmit。 与每个风暴侦听器一样,它可以访问名为 event 的参数。 正如您从第 8 章中应该知道的那样,事件实际上是调度的干扰,其中包含有关操作本身的许多有用信息。 让我们实现 this.handleSubmit:
然后,实例化 From 类:
const formSelector = document.forms[0];
new Form(formSelector);
此时,在浏览器中打开 form.html。 输入您的内容并单击“提交”。 会发生什么? 输出如下:
http://localhost:63342/little-javascript/code/ch10/form.html?name=Valentino&description=Trip+to+Spoleto&tak=We%27re+going+to+visit+the+city%21
这是怎么回事? 大多数 DOM 事件都有所谓的“默认行为”。 提交事件专门尝试将表单数据发送到虚构的服务器。 这就是在没有 JS 的情况下发送表单的样子,因为它是基于 Django、Rails 和 Friends 等 Web 框架的应用程序的一部分。
每个输入值都映射到相应的名称属性。 在本例中不需要该名称,因为我们想要使用 JS 来控制表单,因此我们需要禁用默认行为。 可以通过调用 PreventDefault 来禁用此功能:
保存文件并再次刷新 form.html。 尝试填写表格并单击“提交”。 您将听到事件对象复制到控制台:
Event {...}
bubbles: true
cancelBubble: false
cancelable: true
composed: false
currentTarget: null
defaultPrevented: true
eventPhase: 0
isTrusted: true
path: (5) [form, body, html, document, Window]
returnValue: false
srcElement: form
target: form
timeStamp: 8320.840000000317
type: "submit"
在事件对象的众多属性中,还有 event.target,它指示我们的 HTML 表单以及所有输入的保存位置,让我们看看是否确实如此。
从中提取数据
要获取表单的值,通过检查 event.target,您会发现有一个名为 elements 的属性。 该属性是表单中所有元素的集合。 elements 集合是一个有序列表,包含 、 、 和 等形式的所有数组。 如果您尝试使用 console.log(event.target.elements) 进行复制,您将看到:
0: input#name
1: input#description
2: textarea#task
3: button
length: 4
description: input#description
name: input#name
tak: textarea#task
task: textarea#task
每个表单数组出现在元素集合中的顺序与它们在标记中出现的顺序相同,并且可以根据位置和名称属性进行访问。 现在,我们有两种方法来获取输入值:
事实上,如果您现在想要在每个表单元素上添加适当的 id 属性,您可以访问与 event.target.elements.some_id 相同的元素,其中 id 是您分配给该属性的字符串。 由于event.target.elements首先是一个对象,因此还可以使用ES6对象重构:
const { name, description, task } = event.target.elements;
这种方法并不是 100% 推荐的,例如在 TypeScript 中你会得到一个错误,但只要写“vanilla JS”就可以了。 现在我们有了这个值,我们就可以完成handleSubmit了,在此过程中,我们还创建了另一个名为saveData的方法。 现在它只是将值复制到控制台:
这种保存数据的方法并不是最好的判断。 如果数组被修改了怎么办? 现在我们有了名称、任务和描述,但将来我们可能会添加更多输入,因此我们需要动态提取这个数组。 当然,我们还需要解决对象销毁的问题。 我们来看看 event.target.elements
0: input#name
1: input#description
2: textarea#task
3: button
length: 4
description: input#description
name: input#name
tak: textarea#task
task: textarea#task
它看起来像一个领域。 我们使用map方法将其转换为仅包含名称、描述和任务(过滤关键类型提交):
在浏览器中尝试一下并查看控制台:
Uncaught TypeError: event.target.elements.map is not a function
at HTMLFormElement.handleSubmit (form.js:15)
“.map 不是一个函数”。 那么 event.target.elements 到底是什么? 看起来像一个链接列表,但它是一个不同的野兽:它是一个 HTMLFormControlsCollection。 在第 8 章中,我们对此有所了解,并听说某些 DOM 方法返回 HTMLCollection。
// Returns an HTMLCollection
document.chidren;
HTML 集合看起来与链接列表类似,但它们缺少用于迭代其元素的方法(例如映射或过滤器)。 每个元素仍然可以使用方括号表示法来访问,我们可以通过 Array.from 将类似的链表转换为真正的字段:
通过 Array.from 方法将 event.target.elements 构造为字段。 Array.from 接受映射函数作为第二个参数,进一步优化:
刷新 form.html,填写表单,然后按“提交”。 您会在控制台中看到以下字段:
["Valentino", "Trip to Spoleto",
"We're going to visit the city!", undefined]
最后,我想生成一个对象字段,其中每个对象还具有关联表单输入的名称属性:
再次刷新form.html,填写表单,你会看到:
[
{
"name": "name",
"value": "Valentino"
},
{
"name": "description",
"value": "Trip to Spoleto"
},
{
"name": "task",
"value": "We're going to visit the city!"
},
undefined
]
干得好,有一个来自按钮元素的 null 值 undefined。 Map 的默认行为是在“null”值的情况下返回未定义。 由于我们检查了 if (formInput.type !== "submit"),所以按钮元素不会从地图返回,而是被替换为 undefined。 我们可以稍后删除它html表单怎么做,现在让我们看一下localStorage。
了解localStorage并创建类
我们有时需要为用户保留一些数据,这有很多原因。 例如,考虑一个笔记应用程序,用户可以将新内容插入 HTML 表单,然后返回查看此类笔记。 下次她打开页面时,她会找到那里的所有内容。
在浏览器中保存数据有哪些选项? 持久化数据的一种重要方式是使用数据库,但这里我们只有一些 HTML、JS 和浏览器。 然而,现代浏览器中有一个外部工具,就像一个非常简单的数据库,非常适合我们的需求:localStorage。 localStorage 的行为就像一个 JS 对象,它有很多方法:
稍后我们会看到 setItem 和 getItem。 首先我们必须有一个 form.html 文件,其中包含以下内容:
还有相关的JS代码用于拦截提交干扰:
此时,我们需要实现 this.saveData 将每条笔记保存到 localStorage 中。 执行此操作时,您需要尽可能保持通用。 换句话说,我不想用直接保存到 localStorage 的逻辑填充 this.saveData 。
相反,我们为 Form 类提供外部依赖项(另一个类),其作用是实现实际代码。 以后我们把这条笔记信息保存到localStorage还是数据库都没有关系。 对于每个用例,我们应该能够为表单提供不同的“存储”,并根据需求的变化从一种存储转换为另一种存储。 为此,我们首先调整构造函数以接受新的“存储”参数:
现在,随着类的复杂性降低,需要验证构造函数的参数。 作为处理HTML表单的类,我们至少需要检测formSelector是否是form类型的HTML元素:
constructor(formSelector, storage) {
// Validating the arguments
if (!(formSelector instanceof HTMLFormElement))
throw Error(`Expected a form element got ${formSelector}`);
//
this.formSelector = formSelector;
this.storage = storage;
this.init();
}
如果formSelector不是form类型的话,会报错。 还要验证存储,因为我们必须将用户输入存储在某处。
constructor(formSelector, storage) {
// Validating the arguments
if (!(formSelector instanceof HTMLFormElement))
throw Error(`Expected a form element got ${formSelector}`);
// Validating the arguments
if (!storage) throw Error(`Expected a storage, got ${storage}`);
//
this.formSelector = formSelector;
this.storage = storage;
this.init();
}
存储实现将是另一个类。 在我们的例子中,它可能类似于通用的 LocalStorage,在 form.js 中创建 LocalStorage 类:
class LocalStorage {
save() {
return "saveStuff";
}
get() {
return "getStuff";
}
}
现在,有了这个结构,我们就可以连接 Form 和 LocalStorage:
还是在form.js中,修改类如下:
代码部署后可能出现的BUG无法实时得知。 事后为了解决此类BUG,花费了大量的时间在日志调试上。 顺便推荐一下Fundebug,一款好用的BUG监控工具。
原来的:
沟通