表单是Web应用不可缺少的功能,它是服务器端与浏览器端数据交换的最基本的方法。你应该已经读过很多关于HTML5和Javascript的表单标签和数据验证的文章,今天我们来讨论下如何检查表单数据是否被改变了。
为什么要检查表单数据被改变?
有很多理由需要检测表单数据是否有修改,让我们来看一个很常见的场景,如果用户修改了一个或多个表单域的值,当用户跳离当前页面时,你会给出用户类似“你的修改尚未保存”的提示。你甚至可以给出用户是否保存数据(比如Ajax)的选项。另外的情况,如果表单项没有被修改,这时略去冗余的表单验证和数据发送将会增强用户体验。
Javascript onchange事件
为HTML表单元素添加onchange事件处理器是一种可行的方法,这也是用的最多的,但是onchange的实现有一些问题存在:
- 如果用户改变表单域的值,然后再修改回原始值,程序仍将认为表单的修改已经发生。
- 如果表单项的值是通过Javascript动态修改的,onchange事件不会被自动触发。
- 为每一个表单元素增加onchange事件会引起性能问题,特别是较大的表单。
- 如果将表单元素从DOM中增加或移除,你需要相应的注册或移除事件侦听。
- checkbox和radio的onchange事件在某些浏览器下不能按预期工作(你应该知道是哪个浏览器)。
- 除了onchange,还有更简单有效的方案。
比较默认值
幸运的是,我们不需要为了复杂的事件处理而写过多的废话,每个表单元素都有与该对象相关联的默认值,即页面加载完后表单控件显示或者默认的数据值,该值可与用户操作后的值进行对比以得出表单域的值是否发生了改变。
有点麻烦的是,不同类型的表单元素的默认值属性(properties)不尽相同(做些判断好了,并不影响什么)
input和textarea
让我们从简单的元素开始,所有的textarea对象和除了checkbox、radio之外的 input对象都有一个名为defaultValue的属性,我们可以对比文本域的当前值和默认值是否相同以决定文本域的修改是否发生。
<!-- name input --> <input type="text" id="name" name="name" value="www.csser.com" /> <script> var name = document.getElementById("name"); if (n.value != n.defaultValue) alert("#name has changed"); </script>
小提示:HTML4或XHTML中,文本域类型为text、hidden、password或file的对象有defaultValue属性。HTML5新的表单类型也有defaultValue属性并且可以用相同的方式检测默认值是否改变过,这些新表单元素包含email、tel、url、range、date、color和search。
checkbox和radio
checkbox和radio对象有一个defaultChecked属性,其取值为true或false,我们可以通过对比当前选中状态与默认选中状态进行比较,如:
<!-- newsletter opt-in --> <input type="checkbox" id="optin" name="optin" checked="checked" /> <script> var optin = document.getElementById("optin"); if (n.checked != n.defaultChecked) alert("#optin has changed by csser"); </script>
注意checkbox和radio对象也有defaultValue属性,但它仅被分配给元素的value特性(attribute)而不能标识当前的选中状态。
下拉列表select
select控件通常用于提供一个下拉列表供用户选择,相比上面提到的控件,select有一点复杂,select元素本身并不具有默认值属性,它的默认值存在于其option元素集合内。当页面加载完毕,默认选中的option拥有selected特性,也就是这个option对象拥有defaultSelected属性且值为true。
我们可以从select节点的selectedIndex属性取得当前选中的option的索引值,如果该option对象的defaultSelected属性为false,那么select控件的值一定被修改了,如:
<!-- job title select box --> <select id="job" name="job"> <option>web designer</option> <option selected="selected">csser.com developer</option> <option>graphic artist</option> <option>IT professional</option> <option>other</option> </select> <script> var job = document.getElementById("job"); if (!job.options[job.selectedIndex].defaultSelected) alert("#job has changed"); </script>
上面的代码在单选下拉列表并且有一个option拥有selected特性时运行正常,但我们需要留意一些陷阱:
- 如果没有option被赋予selected特性,浏览器会将第一个设为默认选中,但其defaultSelected属性值却为false
- 如果两个或更多option拥有selected特性(这不合逻辑,但是可能的),每个option都会拥有defaultSelected属性且值为true,但是浏览器仅能得到最后一个的默认值。
- 多重选择控件允许用户选择任意个option:
<!-- skills multi-select box --> <select id="skills" name="skills" multiple="multiple"> <option selected="selected">HTML</option> <option selected="selected">CSSer</option> <option selected="selected">JavaScript</option> <option>PHP</option> </select>
多选下拉列表并不常用,因为用多个checkbox可以提供更友好的界面,但是,如果使用多选下拉列表,多个option具有defaultSelected属性且值为true,select节点的selectedIndex属性已经无效,所以需要循环每一个option才能判断其selected属性是否与其defaultSelected属性相匹配。下面的代码可以解决这个问题:
var skills = document.getElementById("skills"),c = false, def = 0, o, ol, opt; for (o = 0, ol = n.options.length; o < ol; o++) { opt = skills.options[o]; c = c || (opt.selected != opt.defaultSelected); if (opt.defaultSelected) def = o; } if (c && !skills.multiple) c = (def != skills.selectedIndex); if (c) alert("#skills has changed");
上面这些就是本文介绍的如何检测一个表单值是否改变的内容。