对于这个由三部分组成的系列有关HTML5 Web表单的最后一篇文章,我们将讨论JavaScript集成和约束验证API。 如果您尚未这样做,请阅读The Markup和CSS文章,以确保您熟悉这些概念。
HTML5允许我们无需任何JavaScript编码即可实现客户端表单验证。 但是,在实现更复杂的表单时,我们需要增强本机验证,因为:
- 并非所有浏览器都支持所有HTML5输入类型和CSS选择器
- 错误消息气泡使用通用文本(“请填写此字段”)并且难以设置样式
-
:invalid
和:required
样式将在用户与表单进行交互之前应用于页面加载。
大量的JavaScript魔术和Constraint Validation API可以改善用户体验。 请注意,如果您想支持我们将竭尽所能的各种浏览器和输入类型,可能会有些混乱。
截取表格
在HTML5之前,客户端验证涉及在表单上附加一个submit
处理程序,该处理程序将验证字段,显示错误并防止Submit事件。
在HTML5中,浏览器将首先执行自己的验证-在表单有效之前,将不会触发submit
事件。 因此,如果您想做一些复杂的事情,例如显示自己的错误,比较或自动填充字段,则必须通过将表单的noValidate
属性设置为true来关闭本机验证:
var form = document.getElementById("myform");
form.noValidate = true;
// set handler to validate the form
// onsubmit used for easier cross-browser compatibility
form.onsubmit = validateForm;
当然,这意味着您必须检查代码中的字段错误,但是仍然可以利用本机浏览器验证,我们将在不久后看到。
字段.willValidate属性
每个输入字段都有一个.willValidate
属性。 返回:
- 当浏览器将本地验证字段时为true
- 当浏览器将不验证该字段时为false ,或者
- 当浏览器不支持本地HTML5验证(例如IE8)时, 未定义 。
由于我们在上面禁用了本机验证,因此每个字段都将返回false。 让我们创建一个validateForm
处理程序,该处理程序遍历所有字段并检查本地验证是否可用:
function validateForm(event) {
// fetch cross-browser event object and form node
event = (event ? event : window.event);
var
form = (event.target ? event.target : event.srcElement),
f, field, formvalid = true;
// loop all fields
for (f = 0; f < form.elements; f++) {
// get field
field = form.elements[f];
// ignore buttons, fieldsets, etc.
if (field.nodeName !== "INPUT" && field.nodeName !== "TEXTAREA" && field.nodeName !== "SELECT") continue;
循环遍历表单的elements
集合中的所有字段,并检查它们是否是输入,而不是其他类型(例如按钮和字段集)。 下一行很重要…
// is native browser validation available?
if (typeof field.willValidate !== "undefined") {
// native validation available
}
else {
// native validation not available
}
false和undefined都是falsey值,因此您不能仅检查field.willValidate
!
现在我们知道第一个块中的代码将评估何时可以使用本机验证。 然而…
浏览器是否支持输入类型?
如果阅读了第一部分 ,您会记得不支持的输入类型会回到text
。 例如:
<input type="date" name="dob" />
Firefox 29或IE11本身不支持。 这些浏览器将(有效)呈现:
<input type="text" name="dob" />
但是两种浏览器都支持text
类型的验证,因此field.willValidate
不会返回undefined ! 因此,我们必须检查我们的type
属性是否与对象的.type
属性匹配-如果不匹配,则需要实施旧版后备验证,例如
// native validation available
if (field.nodeName === "INPUT" && field.type !== field.getAttribute("type")) {
// input type not supported! Use legacy JavaScript validation
}
Field .checkValidity()方法
如果可以使用本机验证,则可以执行.checkValidity()
方法来验证该字段。 如果没有问题,则该方法返回true,否则返回false 。
有一个类似的.reportValidity()
方法,它无需重新检查即可返回当前状态,尽管这种方法用处不大,并非所有浏览器都支持。
两种方法还将:
- 设置字段的
.validity
对象,以便可以更详细地检查错误,并且 - 验证失败时,在字段上触发
invalid
事件。 这可用于显示错误,更改颜色等。请注意,没有相应的valid
事件,因此请记住在必要时重置错误样式和消息。
字段.validity对象
.validity
对象具有以下属性:
.valid
–如果该字段没有错误,则返回true,否则返回false 。 .valueMissing
–如果需要该字段但已输入值,则返回true 。 .typeMismatch
–如果值的语法不正确(例如,格式错误的电子邮件地址),则返回true 。 .patternMismatch
–如果该值与pattern
属性的正则表达式不匹配,则返回true 。 .tooLong
如果该值大于允许的maxlength
则返回true 。 .tooShort
-如果该值大于允许短返回true minlength
。 .rangeUnderFlow
–如果该值小于min
则返回true 。 .rangeOverflow
–如果该值大于max
则返回true 。 .stepMismatch
–如果该值与step
不匹配,则返回true 。 .badInput
–如果条目无法转换为值,则返回true 。 .customError
–如果该字段设置了自定义错误,则返回true 。
并非所有浏览器都支持所有属性,因此请注意不要做太多假设。 在大多数情况下, .valid
或.checkValidity()
的结果应足以显示或隐藏错误消息。
在旧版浏览器中支持.validity
您可以在旧版浏览器中手动模拟.validity
对象,例如
// native validation not available
field.validity = field.validity || {};
// set to result of validation function
field.validity.valid = LegacyValidation(field);
这样可以确保可以在所有浏览器中测试.validity.valid
。
字段.setCustomValidity()方法
可以传递.setCustomValidity()
方法:
- 一个空字符串。 这
.checkValidity()
字段设置为有效,因此.checkValidity()
和.validity.valid
将返回true ,或者 - 一个包含错误消息的字符串,它将显示在消息提示框中(如果使用)。 该消息还会将字段标记为失败,因此
.checkValidity()
和.validity.valid
将返回false,并且将触发invalid
事件。
请注意,您还可以使用字段的.validationMessage
属性检查当前消息。
全部放在一起
现在,我们有了一个简单的通用跨浏览器表单验证系统的基础:
var form = document.getElementById("myform");
form.noValidate = true;
// set handler to validate the form
// onsubmit used for easier cross-browser compatibility
form.onsubmit = validateForm;
function validateForm(event) {
// fetch cross-browser event object and form node
event = (event ? event : window.event);
var
form = (event.target ? event.target : event.srcElement),
f, field, formvalid = true;
// loop all fields
for (f = 0; f < form.elements; f++) {
// get field
field = form.elements[f];
// ignore buttons, fieldsets, etc.
if (field.nodeName !== "INPUT" && field.nodeName !== "TEXTAREA" && field.nodeName !== "SELECT") continue;
// is native browser validation available?
if (typeof field.willValidate !== "undefined") {
// native validation available
if (field.nodeName === "INPUT" && field.type !== field.getAttribute("type")) {
// input type not supported! Use legacy JavaScript validation
field.setCustomValidity(LegacyValidation(field) ? "" : "error");
}
// native browser check
field.checkValidity();
}
else {
// native validation not available
field.validity = field.validity || {};
// set to result of validation function
field.validity.valid = LegacyValidation(field);
// if "invalid" events are required, trigger it here
}
if (field.validity.valid) {
// remove error styles and messages
}
else {
// style field, show error, etc.
// form is invalid
formvalid = false;
}
}
// cancel form submit if validation fails
if (!formvalid) {
if (event.preventDefault) event.preventDefault();
}
return formvalid;
}
// basic legacy validation checking
function LegacyValidation(field) {
var
valid = true,
val = field.value,
type = field.getAttribute("type"),
chkbox = (type === "checkbox" || type === "radio"),
required = field.getAttribute("required"),
minlength = field.getAttribute("minlength"),
maxlength = field.getAttribute("maxlength"),
pattern = field.getAttribute("pattern");
// disabled fields should not be validated
if (field.disabled) return valid;
// value required?
valid = valid && (!required ||
(chkbox && field.checked) ||
(!chkbox && val !== "")
);
// minlength or maxlength set?
valid = valid && (chkbox || (
(!minlength || val.length >= minlength) &&
(!maxlength || val.length <= maxlength)
));
// test pattern
if (valid && pattern) {
pattern = new RegExp(pattern);
valid = pattern.test(val);
}
return valid;
}
LegacyValidation
特意保留为简单; 它会检查required
, minlength
, maxlength
和pattern
正则表达式,但是您将需要其他代码来检查电子邮件,URL,日期,数字,范围等。
这就引出了一个问题: 如果您要为旧版浏览器编写现场验证代码,为什么还要麻烦使用本机浏览器API? 很好! 仅当您想支持IE6以上的所有浏览器并提供类似的用户体验时,才需要上面的代码。 并非总是必要的……
- 对于简单的表单,您可能不需要任何JavaScript代码。 那些使用旧版浏览器的用户可能会退回到服务器端验证-应该始终予以实施。
- 如果您需要更复杂的表单,但只需要支持最新的浏览器(IE10 +),则可以删除所有旧的验证代码。 如果您的表单使用的日期是Firefox和IE当前不支持的日期,则只需使用其他JavaScript。
- 即使您确实需要代码来检查IE9及以下版本中的电子邮件,数字等字段,也请使其保持简单,并在停止支持这些浏览器后将其删除。 现在有点混乱,但是情况会有所改善。
但是请记住,始终使用正确的HTML5字段类型。 这些浏览器将提供本机输入控件,并在禁用JavaScript的情况下强制执行更快的客户端验证。