在设计 HTML 表单的时候,通常的结构都是一个 form 里面包含了各种 input / select / button 等字段元素,这样就可以把元素和表单关联起来了。但如果字段在 form 的外面呢?在 HTML5 中引入的 form 属性,可以显式地给字段指定一个“目的地”表单。
例如如下代码:
<form id="contact_form" method="get">
<label>Name:</label><input type="text" id="name" name="name">Email:</label>
<input type="email" id="email" name="email">
<input type="submit" id="submit" value="SEND">
</form>
<textarea id="comments" form="contact_form"></textarea>
可是……为什么要把元素放在外面?这样岂不是无论语义上还是结构上都更难理解?
一开始我也觉得这是一个鸡肋的功能,直到后来遇到了两难的情况。
当一个表单中出现多个值不同的 input[type=submit]
时,默认按钮取决于 HTML 中第一个出现的按钮。按下回车时会触发一次默认按钮的 onclick,然后才是表单的 onsubmit。这样一来我的实现代码出现了一些棘手的 bug,具体细节跟本文无关,此处不表。最后只好把提交按钮放到 form 之外并用这一特性进行关联。
项目不打算对 IE 进行向下兼容,看样子是没事了。不过问题又来了,IE11 居然也不支持。
在我的需求里仅仅是模拟一个 submit 按钮,所以兼容起来很简单:
$('[type=submit][form]').click(function() {
var formId = $(this).attr('form'),
form = $('#' + formId);
field = $('<input type="text" />').attr('type', 'hidden').attr('name', this.name).val(this.value);
form.append(field).submit();
});
如果需要实现一个通用的 polyfill 呢?
这个属性的行为可以用一句话带过——关联表单和字段,不过具体实现起来至少需要完成4个任务:
这里面最麻烦的是字段值的同步,因为要处理文本框、单选、复选、下拉框等多种控件。还好这个轮子已经有人造好了,那就是 jQuery 的 serializeArray
方法:
https://github.com/jquery/jquery/blob/10399ddcf8a239acc27bdec9231b996b178224d3/src/serialize.js#L87
已知一个表单,要获取所有在 form 元素之外并设置了 form 属性的字段值,简单地调用即可:
var data = $('[form='+ this.id + ']').serializeArray();
此外,这个方法的实现代码也让我们剩下的“重置”和“提交”功能有了思路。
模拟 input[type=reset] 行为手工重置字段的值有三种情况:
this.value
为 this.defaultValue
this.checked
为 this.defaultChecked
this.selected
为 this.defaultSelected
最后实现代码如下,初步测试 XP+IE6 通过:
(function($) {
/**
* polyfill for html5 form attr
*/
// detect if browser supports this
if (window.HTMLFormElement && $('[form]').get(0).form instanceof HTMLFormElement) {
// browser supports it, no need to fix
return;
}
/**
* Append a field to a form
*
*/
$.fn.appendField = function(data) {
// for form only
if (!this.is('form')) return;
// wrap data
if (!$.isArray(data) && data.name && data.value) {
data = [data];
}
var $form = this;
// attach new params
$.each(data, function(i, item) {
$('<input/>')
.attr('type', 'hidden')
.attr('name', item.name)
.val(item.value).appendTo($form);
});
return $form;
};
/**
* Find all input fields with form attribute point to jQuery object
*
*/
$('form[id]').submit(function(e) {
// serialize data
var data = $('[form='+ this.id + ']').serializeArray();
// append data to form
$(this).appendField(data);
}).each(function() {
var form = this,
$fields = $('[form=' + this.id + ']');
$fields.filter('button, input').filter('[type=reset],[type=submit]').click(function() {
var type = this.type.toLowerCase();
if (type === 'reset') {
// reset form
form.reset();
// for elements outside form
$fields.each(function() {
this.value = this.defaultValue;
this.checked = this.defaultChecked;
}).filter('select').each(function() {
$(this).find('option').each(function() {
this.selected = this.defaultSelected;
});
});
} else if (type.match(/^submit|image$/i)) {
$(form).appendField({name: this.name, value: this.value}).submit();
}
});
});
})(jQuery);
有点小题大做,不过也顺便深入研究了一下浏览器对表单的处理。请轻喷~
扫码关注w3ctech微信公众号
代码请使用 ``` 的方式。
通过四个空格不是特别好用。
已改 ╮(╯▽╰)╭
最后实现代码如下,初步测试 XP+IE6 通过:
上面这段话以下的代码,也需要修改哈。 现在有一些还单独在外面。
共收到3条回复