# 操作表單
用JavaScript操作表單和操作DOM是類似的,因為表單本身也是DOM樹。
不過表單的輸入框、下拉框等可以接收用戶輸入,所以用JavaScript來操作表單,可以獲得用戶輸入的內容,或者對一個輸入框設置新的內容。
HTML表單的輸入控件主要有以下幾種:
* 文本框,對應的`<input type="text">`,用于輸入文本;
* 口令框,對應的`<input type="password">`,用于輸入口令;
* 單選框,對應的`<input type="radio">`,用于選擇一項;
* 復選框,對應的`<input type="checkbox">`,用于選擇多項;
* 下拉框,對應的`<select>`,用于選擇一項;
* 隱藏文本,對應的`<input type="hidden">`,用戶不可見,但表單提交時會把隱藏文本發送到服務器。
## 獲取值
如果我們獲得了一個`<input>`節點的引用,就可以直接調用`value`獲得對應的用戶輸入值:
```
// <input type="text" id="email">
var input = document.getElementById('email');
input.value; // '用戶輸入的值'
```
這種方式可以應用于`text`、`password`、`hidden`以及`select`。但是,對于單選框和復選框,`value`屬性返回的永遠是HTML預設的值,而我們需要獲得的實際是用戶是否“勾上了”選項,所以應該用`checked`判斷:
```
// <label><input type="radio" name="weekday" id="monday" value="1"> Monday</label>
// <label><input type="radio" name="weekday" id="tuesday" value="2"> Tuesday</label>
var mon = document.getElementById('monday');
var tue = document.getElementById('tuesday');
mon.value; // '1'
tue.value; // '2'
mon.checked; // true或者false
tue.checked; // true或者false
```
## 設置值
設置值和獲取值類似,對于`text`、`password`、`hidden`以及`select`,直接設置`value`就可以:
```
// <input type="text" id="email">
var input = document.getElementById('email');
input.value = 'test@example.com'; // 文本框的內容已更新
```
對于單選框和復選框,設置`checked`為`true`或`false`即可。
## HTML5控件
HTML5新增了大量標準控件,常用的包括`date`、`datetime`、`datetime-local`、`color`等,它們都使用`<input>`標簽:
```
<input type="date" value="2015-07-01">
```
```
<input type="datetime-local" value="2015-07-01T02:03:04">
```
```
<input type="color" value="#ff0000">
```
不支持HTML5的瀏覽器無法識別新的控件,會把它們當做`type="text"`來顯示。支持HTML5的瀏覽器將獲得格式化的字符串。例如,`type="date"`類型的`input`的`value`將保證是一個有效的`YYYY-MM-DD`格式的日期,或者空字符串。
## 提交表單
最后,JavaScript可以以兩種方式來處理表單的提交(AJAX方式在后面章節介紹)。
方式一是通過`<form>`元素的`submit()`方法提交一個表單,例如,響應一個`<button>`的`click`事件,在JavaScript代碼中提交表單:
```
<!-- HTML -->
<form id="test-form">
<input type="text" name="test">
<button type="button" onclick="doSubmitForm()">Submit</button>
</form>
<script>
function doSubmitForm() {
var form = document.getElementById('test-form');
// 可以在此修改form的input...
// 提交form:
form.submit();
}
</script>
```
這種方式的缺點是擾亂了瀏覽器對form的正常提交。瀏覽器默認點擊`<button type="submit">`時提交表單,或者用戶在最后一個輸入框按回車鍵。因此,第二種方式是響應`<form>`本身的`onsubmit`事件,在提交form時作修改:
```
<!-- HTML -->
<form id="test-form" onsubmit="return checkForm()">
<input type="text" name="test">
<button type="submit">Submit</button>
</form>
<script>
function checkForm() {
var form = document.getElementById('test-form');
// 可以在此修改form的input...
// 繼續下一步:
return true;
}
</script>
```
注意要`return true`來告訴瀏覽器繼續提交,如果`return false`,瀏覽器將不會繼續提交form,這種情況通常對應用戶輸入有誤,提示用戶錯誤信息后終止提交form。
在檢查和修改`<input>`時,要充分利用`<input type="hidden">`來傳遞數據。
例如,很多登錄表單希望用戶輸入用戶名和口令,但是,安全考慮,提交表單時不傳輸明文口令,而是口令的MD5。普通JavaScript開發人員會直接修改`<input>`:
```
<!-- HTML -->
<form id="login-form" method="post" onsubmit="return checkForm()">
<input type="text" id="username" name="username">
<input type="password" id="password" name="password">
<button type="submit">Submit</button>
</form>
<script>
function checkForm() {
var pwd = document.getElementById('password');
// 把用戶輸入的明文變為MD5:
pwd.value = toMD5(pwd.value);
// 繼續下一步:
return true;
}
</script>
```
這個做法看上去沒啥問題,但用戶輸入了口令提交時,口令框的顯示會突然從幾個`*`變成32個`*`(因為MD5有32個字符)。
要想不改變用戶的輸入,可以利用`<input type="hidden">`實現:
```
<!-- HTML -->
<form id="login-form" method="post" onsubmit="return checkForm()">
<input type="text" id="username" name="username">
<input type="password" id="input-password">
<input type="hidden" id="md5-password" name="password">
<button type="submit">Submit</button>
</form>
<script>
function checkForm() {
var input_pwd = document.getElementById('input-password');
var md5_pwd = document.getElementById('md5-password');
// 把用戶輸入的明文變為MD5:
md5_pwd.value = toMD5(input_pwd.value);
// 繼續下一步:
return true;
}
</script>
```
注意到`id`為`md5-password`的`<input>`標記了`name="password"`,而用戶輸入的`id`為`input-password`的`<input>`沒有`name`屬性。沒有`name`屬性的`<input>`的數據不會被提交。
## 練習
利用JavaScript檢查用戶注冊信息是否正確,在以下情況不滿足時報錯并阻止提交表單:
* 用戶名必須是3-10位英文字母或數字;
* 口令必須是6-20位;
* 兩次輸入口令必須一致。
```
<!-- HTML結構 -->
<form id="test-register" action="#" target="_blank" onsubmit="return checkRegisterForm()">
<p id="test-error" style="color:red"></p>
<p>
用戶名: <input type="text" id="username" name="username">
</p>
<p>
口令: <input type="password" id="password" name="password">
</p>
<p>
重復口令: <input type="password" id="password-2">
</p>
<p>
<button type="submit">提交</button> <button type="reset">重置</button>
</p>
</form>
```
```
'use strict';
var checkRegisterForm = function () {
// TODO:
return false;
}
// 測試:
;(function () {
window.testFormHandler = checkRegisterForm;
var form = document.getElementById('test-register');
if (form.dispatchEvent) {
var event = new Event('submit', {
bubbles: true,
cancelable: true
});
form.dispatchEvent(event);
} else {
form.fireEvent('onsubmit');
}
})();
```
- JavaScript教程
- JavaScript簡介
- 快速入門
- 基本語法
- 數據類型和變量
- 字符串
- 數組
- 對象
- 條件判斷
- 循環
- Map和Set
- iterable
- 函數
- 函數定義和調用
- 變量作用域
- 方法
- 高階函數
- map/reduce
- filter
- sort
- 閉包
- 箭頭函數
- generator
- 標準對象
- Date
- RegExp
- JSON
- 面向對象編程
- 創建對象
- 原型繼承
- 瀏覽器
- 瀏覽器對象
- 操作DOM
- 更新DOM
- 插入DOM
- 刪除DOM
- 操作表單
- 操作文件
- AJAX
- Promise
- Canvas
- jQuery
- 選擇器
- 層級選擇器
- 查找和過濾
- 操作DOM
- 修改DOM結構
- 事件
- 動畫
- 擴展
- underscore
- Collections
- Arrays
- Functions
- Objects
- Chaining
- Node.js
- 安裝Node.js和npm
- 第一個Node程序
- 模塊
- 基本模塊
- fs
- stream
- http
- buffer
- Web開發
- koa
- mysql
- swig
- 自動化工具
- 期末總結
- Python 2.7教程
- Python簡介
- 安裝Python
- Python解釋器
- 第一個Python程序
- 使用文本編輯器
- 輸入和輸出
- Python基礎
- 數據類型和變量
- 字符串和編碼
- 使用list和tuple
- 條件判斷和循環
- 使用dict和set
- 函數
- 調用函數
- 定義函數
- 函數的參數
- 遞歸函數
- 高級特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 函數式編程
- 高階函數
- map/reduce
- filter
- sorted
- 返回函數
- 匿名函數
- 裝飾器
- 偏函數
- 模塊
- 使用模塊
- 安裝第三方模塊
- 使用__future__
- 面向對象編程
- 類和實例
- 訪問限制
- 繼承和多態
- 獲取對象信息
- 面向對象高級編程
- 使用__slots__
- 使用@property
- 多重繼承
- 定制類
- 使用元類
- 錯誤、調試和測試
- 錯誤處理
- 調試
- 單元測試
- 文檔測試
- IO編程
- 文件讀寫
- 操作文件和目錄
- 序列化
- 進程和線程
- 多進程
- 多線程
- ThreadLocal
- 進程 vs. 線程
- 分布式進程
- 正則表達式
- 常用內建模塊
- collections
- base64
- struct
- hashlib
- itertools
- XML
- HTMLParser
- 常用第三方模塊
- PIL
- 圖形界面
- 網絡編程
- TCP/IP簡介
- TCP編程
- UDP編程
- 電子郵件
- SMTP發送郵件
- POP3收取郵件
- 訪問數據庫
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web開發
- HTTP協議簡介
- HTML簡介
- WSGI接口
- 使用Web框架
- 使用模板
- 協程
- gevent
- 實戰
- Day 1 - 搭建開發環境
- Day 2 - 編寫數據庫模塊
- Day 3 - 編寫ORM
- Day 4 - 編寫Model
- Day 5 - 編寫Web框架
- Day 6 - 添加配置文件
- Day 7 - 編寫MVC
- Day 8 - 構建前端
- Day 9 - 編寫API
- Day 10 - 用戶注冊和登錄
- Day 11 - 編寫日志創建頁
- Day 12 - 編寫日志列表頁
- Day 13 - 提升開發效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 編寫移動App
- 期末總結
- Python3教程
- Python簡介
- 安裝Python
- Python解釋器
- 第一個Python程序
- 使用文本編輯器
- Python代碼運行助手
- 輸入和輸出
- Python基礎
- 數據類型和變量
- 字符串和編碼
- 使用list和tuple
- 條件判斷
- 循環
- 使用dict和set
- 函數
- 調用函數
- 定義函數
- 函數的參數
- 遞歸函數
- 高級特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 迭代器
- 函數式編程
- 高階函數
- map/reduce
- filter
- sorted
- 返回函數
- 匿名函數
- 裝飾器
- 偏函數
- 模塊
- 使用模塊
- 安裝第三方模塊
- 面向對象編程
- 類和實例
- 訪問限制
- 繼承和多態
- 獲取對象信息
- 實例屬性和類屬性
- 面向對象高級編程
- 使用__slots__
- 使用@property
- 多重繼承
- 定制類
- 使用枚舉類
- 使用元類
- 錯誤、調試和測試
- 錯誤處理
- 調試
- 單元測試
- 文檔測試
- IO編程
- 文件讀寫
- StringIO和BytesIO
- 操作文件和目錄
- 序列化
- 進程和線程
- 多進程
- 多線程
- ThreadLocal
- 進程 vs. 線程
- 分布式進程
- 正則表達式
- 常用內建模塊
- datetime
- collections
- base64
- struct
- hashlib
- itertools
- XML
- HTMLParser
- urllib
- 常用第三方模塊
- PIL
- virtualenv
- 圖形界面
- 網絡編程
- TCP/IP簡介
- TCP編程
- UDP編程
- 電子郵件
- SMTP發送郵件
- POP3收取郵件
- 訪問數據庫
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web開發
- HTTP協議簡介
- HTML簡介
- WSGI接口
- 使用Web框架
- 使用模板
- 異步IO
- 協程
- asyncio
- async/await
- aiohttp
- 實戰
- Day 1 - 搭建開發環境
- Day 2 - 編寫Web App骨架
- Day 3 - 編寫ORM
- Day 4 - 編寫Model
- Day 5 - 編寫Web框架
- Day 6 - 編寫配置文件
- Day 7 - 編寫MVC
- Day 8 - 構建前端
- Day 9 - 編寫API
- Day 10 - 用戶注冊和登錄
- Day 11 - 編寫日志創建頁
- Day 12 - 編寫日志列表頁
- Day 13 - 提升開發效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 編寫移動App
- FAQ
- 期末總結
- Git教程
- Git簡介
- Git的誕生
- 集中式vs分布式
- 安裝Git
- 創建版本庫
- 時光機穿梭
- 版本回退
- 工作區和暫存區
- 管理修改
- 撤銷修改
- 刪除文件
- 遠程倉庫
- 添加遠程庫
- 從遠程庫克隆
- 分支管理
- 創建與合并分支
- 解決沖突
- 分支管理策略
- Bug分支
- Feature分支
- 多人協作
- 標簽管理
- 創建標簽
- 操作標簽
- 使用GitHub
- 自定義Git
- 忽略特殊文件
- 配置別名
- 搭建Git服務器
- 期末總結