掌握HTML5:约束验证
原创
HTML5 给我们带来了很多非常好的优势。除了统一错误模型、引入新语义标签或简化文档类型等常见问题之外,最大的改进之一是表单的约束验证。如果没有表单,网络会是什么样子?
约束验证试图提高 Web 表单的可用性。浏览器可以直接告知用户无效值的可能性,而不是将表单发送到服务器,然后将其评估为无效,返回到客户端并最终由用户进行调整。这不仅减少了网络通信,还提高了页面的可用性。
需要注意的是,约束验证不能取代服务器端验证。此外,基于 JavaScript 的解决方案可能仍然有用。一般来说,我们总是必须实现服务器端验证。如果我们使用良好的架构,服务器上的模型约束将自动反映在传输的 HTML 代码中。这样我们就可以免费获得约束验证。现在我们可以使用 JavaScript 进一步增强体验,它既可以充当约束验证的补充,也可以充当填充。
我们将从非验证表单开始我们的旅程。然后我们将集成一个基于 JavaScript 的解决方案。最后介绍一下HTML5的约束验证。在最后一节中,我们将了解可能遇到的跨浏览器奇怪现象。
立即学习“前端免费学习笔记(深入)”;
非验证表单提交
最经典的 HTML 表单版本是不带有任何客户端验证逻辑的版本。我们只需要提供一个标准的形式,不需要任何特殊的属性。正如简介中已经指出的,我们需要始终特别注意这种表单提交。
尽管我们确实希望保护客户端上已经存在的表单,但我们永远无法确定所提交数据的状态。保护和增强服务器上的表单验证的技术很大程度上取决于所使用的编程框架和语言。因此我们将跳过这样的讨论。相反,我们现在将讨论一般的表单提交。
在《精通HTML5》系列的第二部分中我们已经提到了表单编码类型的重要性。我们还研究了三种成熟的编码类型。剩下的问题是:这些价值观实际上是如何建立的?浏览器的确切行为取决于为 action 指定的协议。为了简单起见,我们现在假设 HTTP 或 HTTPS。
原则上,浏览器有两个选项:
- 更改操作以携带表单的值。
- 通过请求正文提交值。
两者的程序大致相同。简而言之,我们发现以下步骤:
- 使用正确的编码构建数据集。
- 使用数据集和编码类型创建请求。
- 发送请求。
表单数据集的构建意味着一些微妙的问题,但这些问题并不是很为人所知。例如,如果单击按钮来提交表单,情况就会有所不同。在这种情况下,按钮的值将传输到服务器。这可用于确定按下了哪个按钮。
如果我们按下第一个按钮,那么以下内容将被发送到服务器。
foo=bar
从 JavaScript 触发表单提交将导致不传输任何内容。 JavaScript 代码使用 HTMLFormElement 实例的 submit() 方法。
另一个有趣的方面是使用 image 类型提交输入元素的单击坐标。 image 输入类型不久前非常流行,人们认为检查用户点击的位置是个好主意。也许所显示的图像表明了几种可能性。然后服务器将负责评估用户的请求。
以下示例说明了此行为。
如果我们点击图片提交表单,就会考虑foo的数据。仅当值存在时才会插入名称-值对。此外,我们需要命名输入元素,否则不会传输任何内容。
请求的内容可能类似于以下代码片段。
foo.x=71&foo.y=38&foo=bar
此外,我们应该注意不考虑禁用字段。这是有道理的。因此,下面的表格考虑了具有两个输入字段(一个启用和一个禁用)的前两个示例,可以构建为概念证明。
以编程方式提交表单将导致传输单个值。
基本表单验证
即使没有约束验证或 JavaScript,浏览器也已经为我们提供了一些简单的表单验证。正如我们之前所看到的,表单的状态(例如启用或禁用)和提交者都会被考虑在内。但是,这些都不会阻止表单的提交。一个简单的方法是编写一些 JavaScript 来处理可能中止进程的情况。
JavaScript 的最初用途之一实际上是为表单提供增强的功能。基本思想是在即将提交表单时收到事件通知。此时我们可以检查所有值并中止该过程。当然,我们可以改进整个想法,以便在任何值发生变化时始终进行检查。尽管如此,最终我们可能会根据我们最后的评估而中止提交。
var form = document.querySelector('form'); form.addEventListener('submit', function (ev) { // always abort! ev.preventDefault(); }, false);
理论上进行实时验证很容易。然而,指定的 DOM 事件的工作方式可能与直观猜测的不同。例如,文本框的 change 事件仅在文本框失去焦点后才会触发。当用户单击提交按钮时可能会发生这种情况。因此,与验证的交互被破坏并且感觉不活跃。
相反,使用 keyup 或 input 事件是有意义的。虽然前者是文本框的有效解决方案,但后者适用于所有输入元素(如预期)。唯一的限制是它是随 HTML5 引入的,某些较旧的浏览器可能不支持。
考虑到这一点,让我们比较各个事件以查看执行顺序。下面的测试代码可以帮助我们。
var input = document.querySelector('input'); ['input', 'keyup', 'change'].forEach(function (eventName) { input.addEventListener(eventName, function (e) { console.log(eventName + ' event triggered'); }, false); });
对于我们的测试 元素,当使用几个字母进行探测时,我们会看到以下结果。最后我们使用 Tab 键显式地移开焦点。
正如我们所看到的,顺序设置为首先触发 input 事件,然后触发 keyup。其实这是有道理的。首先我们需要 keydown,然后该值可能会发生变化,从而导致 input 事件。最后我们释放密钥,这会产生一个 keyup 事件。值得强调的是,input 仅在值发生变化时才会触发,而 keyup 与实际值变化无关。举个例子,如果我们按箭头键,我们只会看到 keyup 事件,而看不到 input 事件。
可以通过向所有表单字段添加事件侦听器来对所有元素进行实时验证。或者,我们只需要为表单添加一个用于 input 事件的事件侦听器。尽管非常优雅,但这种方法有一个明显的缺点。
考虑以下非常简单的 HTML:
我们使用 HTML5 form 属性在其外部声明