深入理解 HTML 表单
从 HTML 到 HTML5, 表单相关的元素已经得到了很大的扩充, 基本能够满足我们常见的需求。但在实际工作中, 因为交互或者浏览器兼容的需要, 有时候不得不对原生的表单元素进行扩展或者模拟。但在此之前, 清楚的了解并掌握各种表单元素还是很重要的。在本文中, 我们将对表单元素 (默认是指 HTML5 表单元素)进行详细的阐述。
form
●form 会自动处理 submit 事件 (submit 事件通常由 type=submit 的 input 或者 button 的元素触发)
●form 会自动做一层校验,使用 form.novalidate 可以关闭原生的 validate
●form 会根据每一个表单元素的 name 取得对应的用户输入, 然后将 form data 以 query string 的形式添加到 action 属性对应的 url 后面。默认的请求方法是 GET, 默认的action 是当前的 url。
●event.target.elements 将会返回所有可交互的元素
<form novalidate> <input name='username' required/> <input name='passworkd' type='password' required/> <input name='email' type='email'/> <input type='submit' value='submit'/> </form>
运行上面的代码可以看到, 提交表单之后,浏览器的地址会增加类似这样的 query string ?username=tom&passworkd=123&email=test%40gmail.com
可交互型 elements
需要跟用户进行交互,并获得用户输入的这一类表单元素,我们把它们归类为 可交互型表单元素。
下面列举出来了一些:
●input: 常用的 type 有 checkbox, tel, number, email 等
●textarea
●select
●option
反馈型 elements
只是单纯地反馈信息, 不需要跟用户进行交互的表单元素,我们把它们归类为 反馈型表单元素。
下面列举出来了一些:
●meter
●output
<form oninput="result.value=parseInt(a.value)+parseInt(b.value)"> <input type='number' value='50' name='a' /> <input type='number' value='10' name='b' /> <output name='result'>60</output> </form>
对于 output, 可以在 form.oninput 设置计算出来的 value
分组型 elements
用来对多个表单元素进行分组的这一类表单元素, 我们把他们归类为 分组型表单元素。
下面列举出来了一些:
●fieldset
●optgroup
<form> <select> <optgroup label='group1'> <option>1</option> <option>2</option> <option>3</option> </optgroup> <optgroup label='group2'> <option>4</option> <option>5</option> <option>6</option> </optgroup> <optgroup label='group3'> <option>7</option> <option>8</option> <option>9</optioin> </optgroup> </select> </form>
label
●用 for 可与对应关联了 id 的交互 element 相连
●可以用来包裹可交互 elment, 包括多个, 控制第一个
●不建议嵌套 label
<form> <fieldset> <legend>Title</legend> <label for='radio'>Click me</label> <input type='radio' id='radio'/> </fieldset> </form>
<form> <fieldset> <legend>用户信息</legend> <label> 男 <input type='radio' name='gender' id='male' /> </label> <label> 女 <input type='radio' name='gender' id='female'/> </label> </fieldset> </form>
用 JavaScript 处理表单
field 的抽象
最基本的结构:
field: { name: String, value: String || String[] }
●value 的 String[] 通常是用 , 分割后合成为一个 String
●对于复杂结构的 name 可以使用 keyPath
const fromKeyValues = { 'user.name': 'Tom', 'user.phone[0].number': '123456', 'user.phone[0].type': 'mobile' }; const fromValues = { user: { name: 'Tom', phone: [ { number: '123456', type: 'mobile' } ] } };
如果对上面的代码不是很清楚的, 请参考 qs
一个完整的实现
●阻止 form 默认的 submit 事件
●checkbox 需要拿 checked 而不是 value
●select-multiple 需要存多个值
●除了以上几个特殊的交互元素之外, 其他的都默认从 value 中去取值
form.html
<form> <fieldset> <legend>Login</legend> <input name='username' placeholder='username' minlength='2'/> <input name='password' type='password' placeholder='password'/> <label> remember password <input name='checkbox' type='checkbox'/> </label> </fieldset> <fieldset> <p class="gender"> <legend>More Info</legend> <label> 男 <input name='gender' type='radio' value='male' /> </label> <label> 女 <input name='gender' type='radio' value='female' /> </label> </p> <select name='select' multiple> <option>1</option> <optgroup label='2'> <option>2.1</option> <option>2.2</option> </optgroup> </select> </fieldset> <button type='submit'>Submit</button></form>
form.js
var $form = document.querySelector('form'); function getFormValues(form) { var values = {}; var elements = form.elements; // elemtns is an array-like object for (var i = 0; i < elements.length; i++) { var input = elements[i]; if (input.name) { switch (input.type.toLowerCase()) { case 'checkbox': if (input.checked) { values[input.name] = input.checked; } break; case 'select-multiple': values[input.name] = values[input.name] || []; for (var j = 0; j < input.length; j++) { if (input[j].selected) { values[input.name].push(input[j].value); } } break; default: values[input.name] = input.value; break; } } } return values; } $form.addEventListener('submit', function(event) { event.preventDefault(); getFormValues(event.target); console.log(event.target.elements); console.log(getFormValues(event.target)); });