表单脚本--Form

387 阅读11分钟

表单基础

Web表单在HTML中以<form>元素表示,在JS中则以HTMLFormElement类型表示。

HTMLFormElement有自己的属性和方法:

  • acceptCharset 服务器可以接收的字符集

  • action 请求的URL

  • elements 表单中所有空间的HTMLCollection

  • enctype 请求的编码类型,等价于HTML的enctype属性

  • length 表单中空间的数量

  • method HTTP请求的方法类型,通常是'get'或'post'

  • name 表单的名字,等价于HTML的name属性

  • reset() 把表单字段重置为各自的默认值

    除调用方法外,还可以通过元素实现

    // 通用重置按钮
    <input type="reset" value="Reset Form" />
    // 自定义重置按钮
    <button type="reset">Reset Form</button>
    

    表单设计中通常不提倡重置表单。一般来说,提供一个取消按钮,让用户点击返回前一个页面,而不是恢复表单中所有的值来得更直观

  • submit() 提交表单

  • target 用于发送请求和接收响应的窗口的名字

获取对form元素的引用:

// 通过id获取
let form = document.getElementById('form1');
// 获取页面中的第一个表单
let firstForm = document.forms[0];
// 获取名字为‘form1’的表单
let form = document.forms['form1'];
// 较早的浏览器,或者严格向后兼容的浏览器,也会把每个表单的name作为document对象的属性,例如
let form = document.form1; // 不推荐使用,容易出错

提交表单

// 通用提交按钮
<input type="submit" value="Submit Form" />

// 自定义提交按钮
<button type="submit">Submit form</button>

// 图片按钮
<input type="image" src="img.gif" />

如果表单中有上述任何一个按钮,且焦点在表单中某个控件上,则按回车键也可以提交表单

触发submit事件时,可以根据验证表单结果决定是否真的要提交。阻止这个事件的默认行为可以取消提交表单

let form = document.getElementById('myForm');
form.addEventListener('submit', event => {
    // 阻止表单提交
    event.preventDefault;
})

即使表单中不存在提交按钮也不影响表单提交,可以使用form.submit()

通过submit()提交表单时,submit事件不会触发,因此在调用这个方法前要先做数据验证

为防止用户多次点击提交表单,可以在表单提交后禁用提交按钮,或者通过onsubmit事件处理程序取消之后的表单提交

表单字段的公共属性

<fieldset>元素以外,所有表单字段都有一组同样的属性。

表单字段的公共属性和方法如下:

  • disabled 布尔值,表示表单字段是否禁用

  • form 指针,指向表单字段所属的表单。这个属性是只读的

  • name 字符串,这个字段的名字

  • readOnly 布尔值,表示这个字段是否只读

  • tabIndex 数值,表示这个字段在按Tab键时的切换顺序

  • type 字符串,表示字段类型,如"checkbox"、“radio”等

  • value 要提交给服务器的字段值。

这里除了form属性以外,JS可以动态修改任何属性

每个表单字段都有两个公共方法

  • focus() 把浏览器焦点设置到表单字段

    如果表单中第一个字段是typehiddeninput元素,或者该字段被CSS属性displayvisibility隐藏了,使用focus()方法会报错

    HTML5为表单字段增加了autofocus属性,支持的浏览器会自动为带有该属性的元素设置焦点,而无须使用JS

    默认情况下,只能给表单元素设置焦点。不过,通过将tabIndex属性设置为-1再调用focus(),也可以给任意元素设置焦点。只有Opera不支持这个技术

  • blur() 用于从元素上移除焦点

    在浏览器支持readonly属性之前,Web开发者通常会使用这个方法创建只读字段。

表单字段的公共事件

除了鼠标、键盘、变化和HTML事件外,所有字段还支持以下3个事件

  • blur 在字段失去焦点时触发

  • change<input><textarea>元素的value发生变化且失去焦点时触发,或者在<select>元素中选中项发生变化时触发

    change事件则在字段失去焦点,同时value自控件获得焦点后发生变化时触发。

    对于<select>元素,change事件会在用户改变了选中项时触发,不需要控件失去焦点

    通常用于验证用户在字段中输入的内容

  • focus 在字段获得焦点时触发

blurfocus事件会因为用户手动改变字段焦点或调用blur()focus()方法而触发。这两个事件对所有表单都会一视同仁。

文本框

文本选中

inputtextarea都支持select()方法,此方法用于全部选中文本框中的文本

// 文本框一获得焦点就自动选中其中的所有文本
textbox.addEventListener('focus', event => {
    event.target.select();
})

select()方法相对,还有一个select事件。当选中文本框中的文本时,会触发select事件

虽然select事件能够表明有文本被选中,但不能提供选中了哪些文本的信息。

HTML5对此进行了扩展: selectionStartselectionEnd

这两个属性包含基于0的数值,分别表示文本选区的起点和终点

// 获取文本框中选中的文本
function getSelectedText(textbox) {
    return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
}

除了select()方法之外,Firefox最早实现的setSelectionRange()方法也可以在所有文本框中使用。

textbox.value = 'Hello World!';
// 选择所有文本
textbox.setSelectionRange(0, textbox.value.length);

// 选择前3个字符
textbox.setSelectionRange(0, 3);
// 选择第4-6个字符
textbox.setSelectionRange(4, 7);

输入过滤

由于文本框默认并未提供什么验证功能,因此必须通过JS来实现这种输入过滤。

// 屏蔽所有按键的输入
textbox.addEventListener('keypress', event => {
    event.preventDefault();
})
// 只允许输入数字的代码
// String.fromCharCode()把事件的charCode转换为字符串
textbox.addEventListener('keypress', event => {
    if(!/\d/.test(String.fromCharCode(event.charCode))) {
        event.preventDefault();
    }
})
// 虽然keypress事件应该只在按下字符键时才触发,但某些浏览器会在按下其他键时也触发这个事件
textbox.addEventListener('keypress', event => {
    if(!/\d/.test(String.fromCharCode(event.charCode)) && event.charCode > 9 && !event.ctrlKey) {
        event.preventDefault();
    }
})

处理剪贴板

以下是与剪贴板相关的6个事件

  • beforecopy 复制操作发生前触发

  • copy 复制操作发生时触发

  • beforecut 剪切操作发生前触发

  • cut 剪切操作发生时触发

  • beforepaste 粘贴操作发生前触发

  • paste 粘贴操作发生时触发

事件的行为及相关对象会因浏览器而异

通过beforecopybeforecutbeforepaste事件可以在向剪贴板发送或从中检索数据前修改数据。

不过,取消这些事件并不会取消剪贴板操作。要阻止实际的剪贴板操作,必须取消copycutpaste事件

剪贴板上的数据可以通过window对象(IE)或event对象(Firefox、Safari、Chrome)上的clipboardData对象来获取。为了跨浏览器兼容,最好只在剪贴板事件期间使用这个对象。此对象上有3个方法

  • getData(要检索的数据的格式) 从剪贴板检索字符串数据

  • setData(数据类型,要放到剪贴板上的文本)getData()不同的是,SafariChrome不认可“text”类型。只有在IE8及更早版本中调用setData()才有效,其他浏览器会忽略对这个方法的调用

    为抹平差异,可以使用一下跨浏览器的方法

    function getClipboardText(event) {
        let clipboardData = event.clipboardData || window.clipboardData;
        return clipboardData.getData('text');
    }
    function setClipboardText(event, value) {
        return event.clipboardData ? event.clipboardData.setData('text/plain', value) : window.clipboardData.setData('text', value);
    }
    
  • clearData()

不是所有浏览器都支持剪贴板访问,所以有时候更容易屏蔽一个或多个剪贴板操作

HTML5约束验证API

HTML5为浏览器新增了在提交表单前验证数据的能力。

必填字段

任何带有required属性的字段都必须有值,否则无法提交表单。

此属性适用于<input><textarea><select>字段

<input type="text" name="username" required>
// 通过JS检测对应元素的required属性来判断表单字段是否为必填
let isRequired = document.forms[0].elements['username'].required;
// 检测浏览器是否支持required属性
let isRequiredSupported = 'required' in document.createElement('input');

注意,不同浏览器处理必填字段的机制不同。Safari什么也不做,也不会阻止提交表单。其他浏览器会阻止表单提交并在相应字段下面显示有帮助信息的弹窗

新增类型

HTML5为<input>元素增加了几个新的type值,其中两个得到广泛支持的'email'和'url',二者都有浏览器提供的自定义验证

<input type="email" name="email">
<input type="url" name="homepage">

浏览器在匹配模式时都存在问题。最明显的是文本"-@-"会被认为是有效的电子邮件地址

对于这两个新类型,除非应用了required属性,否则空字段是有效的。

另外,指定一个特殊输入类型并不会阻止用于输入无效的值。新类型只是会应用一些默认验证

除了以上两种type,还有数值类型的:numberrangedatetimedatetime-localdatemonthweektime

并非所有主流浏览器都支持这些类型,因此使用时要当心

且这些数值类型,都可以指定min属性和max属性,以及step属性

每个属性在JS中也可以通过对应元素的DOM属性来访问和修改。此外,还有两个方法从当前值加上或减去对应的数值:stepUp(num)stepDown(num)。(默认情况下,步长值会递减或递增1,浏览器还没有实现这些方法)

HTML5为文本字段新增了pattern属性。这个属性用于指定一个正则表达式,用户输入的文本必须与之匹配。

// 限制只能输入数字
<input type="text" pattern="\d+" name="count">

与新增的输入类型一样,指定pattern属性也不会阻止用户输入无效内容。模式会应用到值,然后浏览器会知道值是否有效

使用checkValidity()方法可以检测表单中任意给定字段是否有效。返回布尔值

// 检查字段是否有效
if(document.forms[0].elements[0].checkValidity()) {
    // 有效
} else {
    // 无效
}
// 检查整个表单是否有效
if(document.forms[0].checkValidity()) {
    // 表单有效,继续
} else {
    // 表单无效
}

checkValidity()方法只会告诉我们字段是否有效,而validity属性会告诉我们字段为什么有效或无效。这个属性是一个对象,包含一系列返回布尔值的属性:

  • customError 如果设置了setCustomValidity()就返回true,否则返回false

  • patternMismatch 如果字段值不匹配指定的pattern属性则返回true

  • rangeOverflow 如果字段值大于max的值则返回true

  • rangeUnderFlow 如果字段值小于min的值则返回true

  • stepMismatch 如果字段值与minmaxstep的值不相符则返回true

  • tooLong 如果字段值的长度超过了maxlength属性指定的值则返回true。(在Firefox4中会自动限制字符串数量,因此这个属性值始终为false

  • typeMismatch 如果字段值不是“email”或“url”要求的格式则返回true

  • valid 如果其他所有属性的值都为false则返回true。与checkValidity()的条件一致

  • valueMissing 如果字段是必填的但没有值则返回true

因此,通过validity属性可以检查表单字段的有效性,从而获取更具体的信息

if(input.validity && !input.validity.valid) {
    if(input.validity.valueMissing){
        console.log('Please specify a value');
    }
}

通过指定novalidate属性可以禁止对表单进行任何验证

<form method="post" action="/signup" novalidate>
    ...
</form>
// 这个值也可以通过JS属性noValidate检索或设置
// 关闭验证
document.forms[0].noValidate = true;

如果一个表单中有多个提交按钮,那么可以给特定的提交按钮添加formnovalidate属性,指定通过该按钮无须验证即可提交表单

<form method="post" action="/signup">
    <input type="submit" formnovalidate name="btnNoValidate" value="Non-validating submit">
</form>
// 关闭验证
document.forms[0].elements['btnNoValidate'].formNoValidate = true;

选择框

选择框是使用<select><option>元素创建的。HTMLSelectElement类型额外提供以下属性和方法:

  • add(newOption, relOption)relOption之前向控件中添加新的option
  • multiple 布尔值,表示是否允许多选
  • options 控件中所有<option>元素的HTMLCollection
  • remove(index) 移除给定位置的选项
  • selectedIndex 选中项基于0的索引值。如果没有选中项则为-1。对于允许多选的列表,始终是第一个选项的索引
  • size 选择框中可见的行数。

选择框的type属性可能是'select-one'或'select-multipe'。

每个<option>元素在DOM中都由一个HTMLOptionElement对象表示,此类型为方便数据存取添加了以下属性:

  • index 选项在options集合中的索引

  • label 选项的标签,等价于HTML的label属性

  • selected 布尔值,表示是否选中了当前选项。

    要取得所有选中项,需要循环选项集合逐一检测selected属性

  • text 选项的文本

  • value 选项的值

在操作选项时,最好使用特定于选项的属性,因为这些属性得到了跨浏览器的良好支持。

选择框的change事件与其他表单字段是不一样的。其他表单字段会在自己的值改变后触发change事件,然后字段失去焦点。而选择框会在选中一项时立即触发change事件