表單腳本

本章主要介紹:表單潮罪、文本框驗(yàn)證與交互沃暗、使用其他表單控制嚼黔。這一章會(huì)繼續(xù)沿用上一章 封裝的 EventUtil 對(duì)象(具體參考前面的事件)

JavaScript最初的一個(gè)應(yīng)用,就是分擔(dān)服務(wù)器處理表單的責(zé)任碎节,打破處處依賴服務(wù)器的局面

一、表單的基礎(chǔ)知識(shí)

JavaScript中创坞,表單對(duì)應(yīng)的是 HTMLFormElement 類型偎谁。HTMLFormElement 繼承了 HTMLElement。HTMLFormElement 有自己下列獨(dú)有的屬性和方法

  • acceptCharset:服務(wù)器能夠處理的字符集:等價(jià)于HTML中的 accpet-charset 特性
  • action:接受請(qǐng)求的URL:等價(jià)于HTML中的 action 特性。
  • elements:表單中所有控件的集合(HTMLCollection)
  • enctype:請(qǐng)求的編碼類型正蛙;等價(jià)于HTML 中的 enctype 特性
  • length:表單中控件的數(shù)量
  • method:要發(fā)送的HTTP請(qǐng)求類型,通常是“get” 或 “post”狂塘;等價(jià)于 HTML 的method特性。
  • name:表單的名稱;等價(jià)于HTML 的name 特性
  • reset():將所有表單域重置為默認(rèn)值
  • submit():提交表單
  • target:用于發(fā)送請(qǐng)求和接受響應(yīng)的窗口名稱窖梁;等價(jià)于 HTML 的target 特性。

取得<form>元素 引用的方式有好幾種:最常見的方式是 使用 getElementById() 方法找到它

var form = document.getElementById('myForm')

其次,通過document.forms 獲取頁面中所有的表單,再通過 索引 或 name值 來訪問

var firstForm = document.forms[0] // 獲取頁面中的第一個(gè)表單
var myForm = document.forms['form2'] // 獲取頁面中 名稱為 “form2” 的表單

在較早的瀏覽器或者那些支持向后兼容的瀏覽器中惧蛹,也會(huì)把每個(gè)設(shè)置了 name 特性的 表單作為屬性保存在 document 對(duì)象中。通過document.form2 可以訪問到名為 "form2" 的表單。

1.1、提交表單

使用 <input> 或 <button> 都可以定義提交按鈕苫费,只要將其 type 特性的值設(shè)置為 "submit" 即可,而圖像按鈕則是通過將 <input> 的 type 特性值設(shè)置為 “image” 來定義

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

<!-- 自定義提交按鈕 -->
<button type="submit">Submit Form</button>

<!-- 圖像按鈕 -->
<input type="image" src="graphic.gif" />

以這種方式提交表單時(shí)柬泽,瀏覽器會(huì)在將請(qǐng)求發(fā)送給服務(wù)器之前觸發(fā) submit 事件。這樣,我們就有機(jī)會(huì)驗(yàn)證表單數(shù)據(jù),并決定是否允許提交表單害晦。

var form = document.getElementById('myForm')
EventUtil.addHandler(form, 'submit', function(event){
  // 取得事件對(duì)象
  event = EventUtil.getEvent(event)

  // if () { 
  // 阻止默認(rèn)事件
  EventUtil.preventDefault(event)
  // }
})

在JavaScript中鳄逾,以編程方式調(diào)用 submit() 方法也可以提交表單澄者。這種方式無需表單包含提交按鈕赠幕。任何時(shí)候都可以正常提交表單竖慧。

var from = document.getElementById('myForm')

// 提交表單
form.submit()

調(diào)用 submit() 方法提交表單的形式魏蔗,不會(huì)觸發(fā) submit事件廓鞠,因此要記得在調(diào)用此方法之前先驗(yàn)證表單數(shù)據(jù)滋早。

提交表單時(shí)可能出現(xiàn)的最大問題,就是重復(fù)提交表單。為此,可以在在第一次提交表單后就禁用提交按鈕,或者利用 onsubmit 事件處理程序取消后續(xù)的表單提交操作萧吠。

1.2梅忌、重置表單

使用 type 特性值為 “reset”的 <input> 或 <button> 都可以創(chuàng)建重置按鈕琼腔,

<!-- 通用重置按鈕 -->
<input type="reset" value="Reset Form">

<!-- 自定義重置按鈕 -->
<button type="reset">Reset From</button>

也可以使用 JavaScript 的方式 來重置表單

var form = document.getElementById('myForm')

// 重置表單
from.reset()

用戶單擊重置按鈕重置表單時(shí),會(huì)觸發(fā) reset 事件。我們可以在必要時(shí)取消重置操作

/* 阻止重置表單操作 */
var form = document.getElementById('myForn')

form.onreset = function(event) {
  event.preventDefault()
}

1.3、表單字段

每個(gè)表單都有一個(gè) elements 屬性悲敷,該屬性是表單中所有表單元素(字段)的集合。可以按照位置和 name 特性來訪問它們,

var form = document.getElementById('myForm')

// 取得表單中的第一個(gè)字段
var field1 = form.elements[0]

// 取得表單中name 為 textbox1的字段
var field2 = form.elements['textbox1']

// 取得表單中包含的字段的數(shù)量
var fieldCount = from.elements.length

如果使用 name 實(shí)現(xiàn)訪問元素時(shí),多個(gè)表單控件的 name 相同筏勒,就會(huì)返回一個(gè) 以改name 命名的 NodeList邪媳,然后可以通過 索引 訪問這個(gè)集合中的元素
如下

<input type="radio" name="color" value="red"> red
<input type="radio" name="color" value="cyan"> cyan
<input type="radio" name="color" value="violet"> violet

<script>
  var colors = document.forms[0]['color']
  console.log(Object.prototype.toString.call(colors))
  console.log(colors[0]) // 第一個(gè) 單選框
</script>
1.3.1、共有的表單字段屬性

除了 <fieldset> 元素之外逗柴,所有的表單字段都擁有相同的一組屬性屠尊。
表單字段共有的屬性如下。

  • disabled:布爾值闰围,表示當(dāng)前字段是否被禁用
  • form:指向當(dāng)前字段所屬表單的指針运敢;只讀迄沫。
  • name:當(dāng)前字段的名稱
  • readOnly:布爾值盼砍,表示當(dāng)前字段是否只讀
  • tabIndex:表示當(dāng)前字段的切換(tab)序號(hào)
  • type:當(dāng)前字段的類型,入“checkbox“侧戴、”radio“,等等。
  • value:當(dāng)前字段將被提交給服務(wù)器的值。對(duì)文件字段來說,這個(gè)屬性是只讀的,包含文件在計(jì)算機(jī)中的路徑

超級(jí) form屬性之外慨默,可以通過 JavaScript 動(dòng)態(tài)的修改其他屬性的值。

var form = document.getElementById('myForm')
var field = form.elements[0]

// 修改 value 屬性
field.value = "Another value"

// 檢查 form 屬性值
alert(field.form == form) // true

// 把焦點(diǎn)設(shè)置當(dāng)前字段
field.focus()

// 禁用當(dāng)前字段
field.disabled = true

// 修改 type 屬性(不推薦,但對(duì)于 <input> 元素來說是可行的)
field.type = "checkbox"

避免多次提交,最常見的解決方案,就是在第一次單機(jī)后就禁用提交按鈕。

//  避免多次提交表單
EventUtil.addHandler(form, 'submit', function(event) {
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)

  // 取得提交按鈕
  var btn = target.elements['submit-btn']

  // 禁用它
  btn.disabled = true
})

注意不能通過 onclick 事件處理程序來實(shí)現(xiàn)這個(gè)功能,原因是不同瀏覽器之間存在”時(shí)差“:有點(diǎn)瀏覽器會(huì)在觸發(fā)表單的 submit 事件之前觸發(fā) click 事件,意味著會(huì)在提交發(fā)生之間禁用按鈕,結(jié)果永遠(yuǎn)都不會(huì)提交表單


除了 <fieldset> 之外涨椒,所有表單字段都有 type 屬性。對(duì)于 <input> 元素绽媒,這個(gè)值等于 HTML 特性 type 的值蚕冬。對(duì)于其他元素,這個(gè) type 屬性的值如下表所列:

說明 HTML示例 type 屬性的值
單選列表 <select>...</select> "select-one"
多選列表 <select multiple>...</select> "select-multiple"
自定義按鈕 <button>...</button> "submit"
自定義非提交按鈕 <button type="button">...</button> "button"
自定義重置按鈕 <button type="reset">...</button> "reset"
自定義提交按鈕 <button type="submit">...</button> "submit

<input> 和 <button> 元素的 type 屬性是可以動(dòng)態(tài)修改的播瞳,而 <select> 元素的 type 屬性則是只讀的。

1.3.2免糕、共有的表單字段方法

每個(gè)表單字段都有兩個(gè)方法:focus() 和 blur()

focus() 方法用于將瀏覽器的焦點(diǎn)設(shè)置到表單字段,級(jí)激活表單字段石窑,使其可以響應(yīng)鍵盤事件牌芋。為此,可以偵聽頁面的 load 事件松逊,并在該事件發(fā)生時(shí)在表單的第一個(gè)字段上調(diào)用 focus() 方法躺屁,如下:

EventUtil.addHandler(window, 'load' , function(event) {
  document.forms[0],elements[0].focus()
})

需要注意的是:如果 focus() 方法的對(duì)象是一個(gè) type="hidden" 的 <input> 元素的話,那么以上代碼會(huì)導(dǎo)致錯(cuò)誤经宏。另外犀暑,如果使用 CSS 的 display 和 visibility 屬性隱藏了該字段,同樣也會(huì)導(dǎo)致錯(cuò)誤

HTML5 為表單字段新增了一個(gè) autofocus 屬性烁兰。在支持這個(gè)屬性的瀏覽器中耐亏,只要設(shè)置這個(gè)屬性不用 JavaScript就能自動(dòng)把焦點(diǎn)移動(dòng)到相應(yīng)字段。例如:

<input type="text" autofocus />

可以通過 autofocus 屬性獲取 元素上面的 autofocus 的狀態(tài)沪斟,在支持的瀏覽器中如果設(shè)置了為true


blur() 作用是從元素上移走焦點(diǎn)

document.forms[0].elements[0].blur()
1.3.3广辰、共有的表單字段事件

所有的表單字段都支持下列三個(gè)事件

  • blur:當(dāng)前字段市區(qū)焦點(diǎn)時(shí)觸發(fā)
  • change:對(duì)于<input> 和 <textarea> 元素,它們失去焦點(diǎn)且 value 值改變時(shí)觸發(fā)主之;對(duì)于 <select> 元素择吊,在其選項(xiàng)改變時(shí)觸發(fā)
  • focus:當(dāng)前字段獲得焦點(diǎn)時(shí)觸發(fā)

change 事件經(jīng)曾用于驗(yàn)證用戶在字段中輸入的數(shù)據(jù)。
下面例子中槽奕,對(duì)上訴三個(gè)事件簡單使用

var textbox = document.forms[0].elements[0]

EventUtil.addHandler(textbox, "focus", function(event) { // 獲得焦點(diǎn)時(shí) 編程黃色
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)

  if (target.style.backgroundColor != 'red') {
    target.style.backgroundColor = 'yellow'
  }
})

EventUtil.addHandler(textbox, "blur", function(event) { // 失去焦點(diǎn)
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)

  // 驗(yàn)證用戶是否輸入的是數(shù)字
  if (/[^\d]/.test(target.value)) {
    target.style.backgroundColor = 'red'
  } else {
    target.style.backgroundColor = ''
  }
})

EventUtil.addHandler(textbox, "input", function(event) { // value 值變化時(shí)
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)
  console.log(target.value)
  // 驗(yàn)證用戶是否輸入的是數(shù)字
  if (/[^\d]/.test(target.value)) {
    console.log(1)
    target.style.backgroundColor = 'red'
  } else {
    target.style.backgroundColor = ''
  }
})

因?yàn)?change 事件必須 在value值改變且失去焦點(diǎn)才會(huì)觸發(fā)几睛,所以這里用 input 事件來實(shí)時(shí)監(jiān)聽

二、文本框腳本

在 HTML 中粤攒,有兩種方式來表現(xiàn)文本框所森,<input> 、<textarea> 兩種元素

<input>

  • 通過設(shè)置 size 特性琼讽,可以指定文本框中能夠顯示的字符數(shù)
  • 通過value特性必峰,可以設(shè)置文本框的初始值
  • 通過maxlength特性則用于指定文本框 可以接受的最大字符數(shù)

<textarea>

  • rows特性指定文本框的字符行數(shù)
  • cols 特性指定文本框的字符列數(shù)

與 <Input> 元素不同,<textarea>的初始值必須要放在 <textarea></textarea>之間

<textarea rows="25" cols="5"> initial value </textarea>

值得注意的時(shí):在操作 value 屬性時(shí)钻蹬,不建議使用 標(biāo)準(zhǔn)的 DOM 方法(setAttribute())吼蚁,這樣對(duì) value 屬性所作的修改,不一定會(huì)反映在 DOM 中

2.1问欠、選擇文本

上訴兩種文本框都支持 select() 方法肝匆,這個(gè)方法用于選擇文本框中的所有文本。在調(diào)用 select() 方法時(shí)顺献,大多數(shù)瀏覽器都會(huì)將焦點(diǎn)設(shè)置到 文本框中

var textbox = document.forms[0].elements["textarea"]
textbox.select()

在文本框獲得焦點(diǎn)時(shí)選擇其所有文本旗国,這是一種非常常見的做法,特別是在 文本框包含默認(rèn)值的時(shí)候注整。因?yàn)檫@樣做可以讓用戶不必一個(gè)一個(gè)地刪除文本能曾。

EventUtil.addHandler(textbox, "focus", function(event) {
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)

  target.select()
})
2.1.1度硝、select 事件

與 select() 方法對(duì)應(yīng)的,有一個(gè) select 事件寿冕,在選擇了文本框的文本是蕊程,就會(huì)觸發(fā)select事件,而在IE8及更早版本中驼唱,只要用戶選擇了一個(gè)字母(不必釋放鼠標(biāo))藻茂,就會(huì)觸發(fā)select 事件。另外玫恳,在調(diào)用 select() 方法時(shí)也會(huì)觸發(fā) select 事件辨赐。

var textbox = document.forms[0].elements['textbox1']
EventUtil.addHandler(textbox, "select", function(event) {
  console.log("select text")
})

當(dāng)用戶選擇 textbox 中的文本時(shí),就會(huì)觸發(fā)這個(gè)事件 京办,輸出 select text

2.1.2掀序、取得選擇的文本

通過 select 事件我們可以知道用戶什么時(shí)候選擇了文本,但任然不知道用戶選擇了什么文本臂港。 HTML5 通過了一些 擴(kuò)展方案解決了這個(gè)問題森枪。添加了兩個(gè)屬性:selectionStart 和 selectionEnd。 這兩個(gè)屬性中保存的是基于 0 的數(shù)值审孽,表示所選擇文本的范圍(即文本選區(qū)開頭和結(jié)尾的偏移量)县袱。

function getSelectedText(textbox) {
  return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd)
}

// 獲取選區(qū)的文本
var textbox = document.forms[0].elements["textarea"]
EventUtil.addHandler(textbox, "select", function(event) {
  // 調(diào)用方法
  console.log("select Text :" +  getSelectedText(textbox))
})

IE8及之前不支持這兩個(gè)屬性而是提供了另一種方案

IE8及更早的版本中有一個(gè) document.selection 對(duì)象,其中保存著用戶在整個(gè)文檔范圍內(nèi)選擇的文本信息佑力,與 select 事件一起使用的時(shí)候式散,可以假定是用戶選擇了文本框中的文本。要取得選擇的文本打颤,首先必須創(chuàng)建一個(gè)范圍暴拄,然后再將文本從其中提取出來。

function getSelectedText(textbox) {
  if (typeof textbox.selectionstart === 'number') {
    return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd)
  } else if(document.selection) { // 針對(duì) IE
    return document.selection.createRange().text
  }
}
2.1.3编饺、選擇部分文本

HTML5 也為選擇文本框中的部分文本提供了解決方案乖篷,所有文本款都有一個(gè) setSelectionRange() 方法,這個(gè)方法接收兩個(gè)參數(shù):要選擇的第一個(gè)字符的索引透且、選擇的最后一個(gè)字符之后的字符的索引

var textbox = document.forms[0].elements["textarea"]

textbox.value = 'Hello World!'

textbox.select() // 使當(dāng)前 獲取焦點(diǎn)

// 選擇所有文本
textbox.setSelectionRange(0, textbox.value.length)

// 選擇前3個(gè)字符
textbox.setSelectionRange(0, 3)

// 選擇第 4-6 個(gè)字符
textbox.setSelectionRange(4, 7)

要看到選擇的文本撕蔼,必須在調(diào)用 setSelectionRange() 之前或之后立即將焦點(diǎn)設(shè)置到文本框


IE8及更早版本,要選擇文本框中的部分文本秽誊,

  1. 首先使用 IE 在所有文本框是 提供的 createTextRange() 方法創(chuàng)建一個(gè)范圍鲸沮,
  2. 然后 使用 moveStart() 和 moveEnd() 這兩個(gè)范圍方法將范圍移動(dòng)到位
  3. 在調(diào)用這兩個(gè)方法之前,還必須使用 collapse() 將范圍折疊到文本框的開始位置
  4. 使用 范圍的 select() 方法選擇文本
var textbox = document.forms[0].elements["textarea"]

textbox.value = 'Hello World!'

var range = textbox.createTextRange()

// 選擇所有文本
range.collapse(true)
range.moveStart('character', 0)
range.moveEnd('character', textbox.value.length)
range.select()

// 選擇前3個(gè)字符
range.collapse(true)
range.moveStart('character', 0)
range.moveEnd('character', 3)
range.select()

實(shí)現(xiàn)跨瀏覽器編程锅论,結(jié)合上訴兩種方案組合

function selectText(textbox, startIndex, endIndex) {
  if (textbox.setSelectionRange) {
    textbox.setSelectionRange(startIndex, endIndex)
  } else if(textbox.createTextRange) { // IE
    var range = textbox.createTextRange() // 創(chuàng)建范圍
    range.collapse(true) // 折疊范圍
     // 移動(dòng)范圍
    range.moveStart('character', startIndex)
    range.moveEnd('character', endIndex)
    range.select() // 選擇文本
  }

  // 獲取焦點(diǎn)
  textbox.focus()
}

可以像下面這樣使用這個(gè)方法


var textbox = document.forms[0].elements['textarea']

textbox.value = 'Hello World'
// 選擇所有文本
selectText(textbox, 0, textbox.value.length)

2.2讼溺、過濾輸入

我們經(jīng)常會(huì)要求用戶在文本框中輸入特定的數(shù)據(jù),或者輸入特定格式的數(shù)據(jù)最易。綜合運(yùn)用事件和DOM手段怒坯,可以將普通的文本框轉(zhuǎn)換成能夠理解用戶輸入數(shù)據(jù)的功能型控件炫狱。

2.2.1、屏蔽字段

響應(yīng)向文本框中插入字符操作的是 keypress 事件剔猿。因此毕荐,可以通過阻止這個(gè)事件的默認(rèn)行為來屏蔽此類字符。

EventUtil.addHandler(textbox, 'keypress', function(event) {
  event = EventUtil.getEvent(event)
  EventUtil.preventDefault(event)
})

如果只想屏蔽特定字符艳馒,則需要檢測(cè) keypress 事件對(duì)應(yīng)的字符編碼,然后再?zèng)Q定如何響應(yīng)员寇。

// 只允許輸入數(shù)值弄慰。
EventUtil.addHandler(textbox, 'keypress', function(event) {
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)
  var charCode = EventUtil.getCharCode(event) // 獲取當(dāng)前的 鍵碼

  if (! (/\d/.test(String.fromCharCode(charCode)))) { // 獲取鍵碼對(duì)應(yīng)字符在進(jìn)行正則檢測(cè) 取反
    EventUtil.preventDefault(event)
  }
})

僅考慮屏蔽不是數(shù)值的字符還不夠,還要避免屏蔽一些極為常用和必要的鍵蝶锋。所幸的是陆爽,要檢測(cè)這些鍵并不困難。
在Firefox中扳缕,所有由非字符鍵觸發(fā)的 keypress 事件對(duì)應(yīng)的字符編碼為0
在 Safari 以前的版本中慌闭,對(duì)應(yīng)的字符編碼全部為8
為了讓代碼更通用,只要不屏蔽那些字符編碼小于10的鍵即可

// ...
 if (!(/\d/.test(String.fromCharCode(charCode))) && charCode > 9) {
    EventUtil.preventDefault(event)
  }

除此之外還有一個(gè)文本需要處理:復(fù)制躯舔、粘貼及其他操作還有用到 Ctrl 鍵驴剔。在除IE之外的所有瀏覽器中,前面的代碼也會(huì)屏蔽 Ctrl + C粥庄、Ctrl + V丧失,以及其他使用 Ctrl 的組合鍵。因此最后還要添加一個(gè)檢測(cè)條件惜互,以確保用戶沒有按下啊 Ctrl 鍵布讹。

if (! (/\d/.test(String.fromCharCode(charCode))) && charCode > 9 && !event.ctrlKey) {
    EventUtil.preventDefault(event)
}
2.2.2、操作剪貼板

IE 是第一個(gè)支持與剪貼板有關(guān)事件训堆,以及通過 JavaScript 訪問剪貼板數(shù)據(jù)的瀏覽器, HTML5后來也把剪貼板事件納入了規(guī)范

  • beforecopy:在發(fā)生復(fù)制操作當(dāng)前觸發(fā)描验。
  • copy:在發(fā)生復(fù)制操作時(shí)觸發(fā)
  • beforecut:在發(fā)生剪切操作前觸發(fā)
  • cut:在發(fā)生剪切操作時(shí)觸發(fā)
  • beforepaste:在發(fā)生粘貼操作前觸發(fā)
  • paste:在發(fā)生粘貼操作時(shí)觸發(fā)

由于沒有針對(duì)剪貼板操作的標(biāo)準(zhǔn),這些事件及相關(guān)對(duì)象會(huì)因?yàn)g覽器而異坑鱼。在Safari膘流、Chrome 和 Firefox中,beforecopy姑躲、beforecut睡扬、beforepaste 事件只會(huì)在顯示在針對(duì)文本框的上下文菜單(預(yù)期發(fā)生剪貼板事件)的情況下觸發(fā)。但是黍析,IE則會(huì)在觸發(fā) copy卖怜、cut和paste事件之前先觸發(fā)這些事件。至于 copy阐枣、cut和paste事件马靠,只要是在上下文菜單中選擇了相應(yīng)選項(xiàng)奄抽,或者使用了相應(yīng)的鍵盤組合鍵,所有瀏覽器都會(huì)觸發(fā)它們甩鳄。
在實(shí)際的事件發(fā)生之前逞度,通過 beforecopy、beforecut妙啃、beforepaste事件可以在像剪貼板發(fā)送數(shù)據(jù)档泽,或者從剪貼板取得數(shù)據(jù)之前修改數(shù)據(jù)。不過揖赴,取消這些事件并不會(huì)取消對(duì)剪切板的操作——只有取消 copy馆匿、cut、paste 事件燥滑,才能阻止響應(yīng)操作發(fā)生渐北。
要訪問剪貼板中的數(shù)據(jù),可以使用 clipboardData 對(duì)象:在IE中铭拧,這個(gè)對(duì)象是 window 對(duì)象的 屬性赃蛛;而在 Firefox 4+、Safari 和 Chrome 中搀菩,這個(gè)對(duì)象是相應(yīng) event 對(duì)象的屬性呕臂。但是,在Firefox秕磷、Safari诵闭、Chrome 中,只有在處理剪貼板事件期間澎嚣,clipboardData對(duì)象才有效疏尿,這是為了防止對(duì)剪貼板的未授權(quán)訪問;在IE中易桃,則可以隨時(shí)訪問 clipboardData 對(duì)象褥琐。為了確保跨瀏覽器兼容晤郑,最好只在發(fā)生剪貼板事件期間使用這個(gè)對(duì)象敌呈。

clipboardData 對(duì)象有三個(gè)方法

  • getDate() 用于從剪貼板中獲取數(shù)據(jù),

    • 接受一個(gè)參數(shù)造寝,即要取得的數(shù)據(jù)格式磕洪,
      在IE中,有兩種數(shù)據(jù)格式:“text”诫龙、“URL”
      在Firefox析显、Safari、Chrome中签赃,這個(gè)參數(shù)是一種MIME類型谷异,可以使用“text”代表“text/plain”
  • setData() 設(shè)置數(shù)據(jù)

    • 數(shù)據(jù)類型
      IE支持:text分尸、URL
      而 Safaru 和 Chrome 只支持 MIME 類型,不能代替
    • 放在剪貼板中的文本歹嘹。
  • clearData():清除數(shù)據(jù)

在成功將文本放到剪貼板中后箩绍,都會(huì)返回true;否則返回false

為了彌補(bǔ)這些差異尺上,我們可以項(xiàng) EventUtil 中再添加下列方法:

var EventUtil = {
  // 獲取剪貼板中的 文本
  getClipboardText: function(event) {
    var clipboardData = (event.clipboardData || window.clipboardData)
    return clipboardData.getData('text')
  }, 

  // 設(shè)置 剪貼板中的文本
  setClipboardText: function(event, value) {
    if (event.clipboardData) return event.clipboardData.setData('text/plain', value)
    else if(window.clipboardData) return window.clipboardData.setData('text', value)
  }
}

在需要確保粘貼到文本框中的文本中包含某些字符材蛛,或者符合某種格式要求時(shí),能夠訪問剪貼板是非常有用的怎抛。在paste事件中仰税,可以確定剪貼板中的事件是否有效

EventUtil.addHandler(textbox, 'paste', function(event) {
  event = EventUtil.getEvent(event)
  var text = EventUtil.getClipboardText(event)
  console.log(1)
  if (!/^\d*$/.test(text)) {
    EventUtil.preventDefault(event)
  }
})

如上代碼只會(huì)在,粘貼板中文本內(nèi)容只包含數(shù)字的時(shí)候抽诉,才能粘貼

由于并非所有瀏覽器都支持訪問剪貼板,所以更簡單的做法是屏蔽一或多個(gè)剪貼板操作

2.3吐绵、自動(dòng)切換焦點(diǎn)

使用 JavaScript 可以從多個(gè)方面增強(qiáng)表單字段的易用性迹淌。其中,最常用的一種方式就是在用戶填寫完當(dāng)前字段時(shí)己单,自動(dòng)將焦點(diǎn)切換到下一個(gè)字段唉窃。

例如,美國的電話號(hào)碼通常分為三個(gè)部分:區(qū)號(hào)纹笼、局號(hào)纹份、另外4位數(shù)字。為取得完整的電話號(hào)碼廷痘,很多網(wǎng)頁中都會(huì)提供下列3個(gè)文本框蔓涧,如下DOM結(jié)構(gòu)

<input type="text" name="tel1" id="txtTel1" maxlength="3">
<input type="text" name="tel2" id="txtTel2" maxlength="3">
<input type="text" name="tel3" id="txtTel3" maxlength="4">

為了增強(qiáng)易用性,同時(shí)加快數(shù)據(jù)輸入笋额,可以在前一個(gè)文本框中的字符達(dá)到最大數(shù)量后元暴,自動(dòng)將焦點(diǎn)切換到下一個(gè)文本框

function tabForward(event) {
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event) // 獲取當(dāng)前DOM元素

  if (target.value.length == target.maxLength) { // 如果 輸入完畢(最大長度)
    var form = target.form // 獲取 當(dāng)前字段的 form

    for(var i = 0, len = form.elements.length; i < len; i++){ // 遍歷
      if (form.elements[i] == target) {  // 當(dāng)前元素
        if (form.elements[i + 1]) { // 如果存在 下一個(gè)元素
          form.elements[i + 1].focus() // 下一個(gè)元素獲得焦點(diǎn)
          return
        }
      }
    }
  }
}

var textbox1 = document.getElementById('txtTel1')
var textbox2 = document.getElementById('txtTel2')
var textbox3 = document.getElementById('txtTel3')

EventUtil.addHandler(textbox1, 'keyup', tabForward)
EventUtil.addHandler(textbox2, 'keyup', tabForward)
EventUtil.addHandler(textbox3, 'keyup', tabForward)

不過請(qǐng)記住,這些代碼只適用于前面給出的標(biāo)記兄猩,沒有考慮隱藏字段

2.4茉盏、HTML5約束驗(yàn)證API

為了在將表單提交到服務(wù)器之前驗(yàn)證數(shù)據(jù),HTML5新增了一些功能枢冤。有了這些功能鸠姨,幾遍JavaScript被禁用或者由于種種原因未能加載,也可以確毖驼妫基本的驗(yàn)證

2.4.1讶迁、必填字段

required 屬性

<input type="text" name="username" required />

在JavaScript中,通過對(duì)應(yīng)的required 屬性趟咆,可以檢查某個(gè)表單字段是否為必填字段

var isUsernameRequired = document.forms[0].elements["username"].required

另外添瓷,使用下面這行代碼可以測(cè)試瀏覽器是否支持required屬性

var isRequiredSupported = "required" in document.createElement("input")

對(duì)于空著的必填字段梅屉,不同瀏覽器有不同的處理方式。Firefox4和Opera11會(huì)阻止表單提交并在相應(yīng)字段下發(fā)彈出幫助框鳞贷,而Safari5-坯汤、Chrome9-則什么也不做,而且也不會(huì)阻止表單提交搀愧。現(xiàn)在的瀏覽器以便都會(huì)阻止提交

2.4.2惰聂、其他輸入類型

HTML5 為<input> 元素的 type 屬性又增加了幾個(gè)值≡凵福“email”搓幌、“url”是兩個(gè)得到支持最多的類型,各瀏覽器也都給他們?cè)黾恿硕ㄖ频尿?yàn)證機(jī)制迅箩。

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

"email" 類型要求輸入的文本必須符合電子郵件地址的模式溉愁,而"url"類型要求輸入的文本必須符合 URL 模式。


要檢查瀏覽器是否支持這些類型饲趋,可以在 JavaScript中創(chuàng)建一個(gè) <input>元素的type設(shè)置為其中一個(gè)拐揭,再檢測(cè)這個(gè)屬性的值

var input = document.createElement("input")
input.type = "email"

var isEmailSupported = (input.type == "email")

設(shè)置特定的輸入類型并不能阻止用戶輸入無效的值,只是應(yīng)用某些默認(rèn)的驗(yàn)證而已奕塑。

2.4.3堂污、數(shù)值范圍

除了上訴兩個(gè)之外,HTML5 還定義了另外幾個(gè)輸入元素龄砰。這幾個(gè)元素都要求填寫某種基于數(shù)字的值:number盟猖、range、datetime换棚、datetime-local式镐、date、month固蚤、week碟案、time

對(duì)所有這些數(shù)值類型的輸入元素,可以指定 min 屬性(最小的可能值)颇蜡、max屬性(最大的可能值)价说、step(兩個(gè)刻度之間的差值)。想讓用戶只能輸入0到100的值风秤,而且這個(gè)值必須是5的倍數(shù)鳖目,可以這些代碼:

<input type="number" min="0" max="100" step="5" name="count" />

在不同的瀏覽器中,可能會(huì)也可能不會(huì)看到能夠自動(dòng)遞增和遞減的數(shù)值調(diào)節(jié)按鈕(向上和向下按鈕)

2.4.4缤弦、輸入模式

HTML5 為文本字段新增了 pattern 屬性领迈。這個(gè)屬性的值是一個(gè)正則表達(dá)式,用于匹配文本框中的值。
例如:只允許在文本空中輸入數(shù)值

<input type="text" pattern="\d+" name="count" />

注意狸捅,模式的開頭和末尾不用加 ^ 和 $符號(hào)(假定已經(jīng)有了)衷蜓。這兩個(gè)符號(hào)表示輸入的值必須從頭到尾與模式匹配。

與其他輸入類型相似尘喝,指定 pattern 也不能阻止用戶輸入無效的文本磁浇。這個(gè)模式應(yīng)用給值窘问,瀏覽器來判斷值是有效竟稳,還是無效。

在JavaScript中可以通過 pattern 屬性訪問

var pattern = document.forms[0].elements['count'].pattern

使用以下代碼可以檢測(cè)瀏覽器是否支持 pattern 屬性

var isPatternSupported = "pattern" in document.createElement("input")
2.4.5边涕、檢測(cè)有效性

使用 checkValidity() 方法可以檢測(cè)表單中的某個(gè)字段是否有效缔赠。所有表單字段都有這個(gè)方法衍锚,如果字段的值有效,這個(gè)方法返回 true嗤堰,否則返回false戴质。字段的值是否有效的判斷依據(jù)是本節(jié)前面介紹過的那些約束。

if (document.forms[0].elements[0].checkValidity()) {
  // 字段有效 
} else {
  // 字段無效
}

要檢測(cè)整個(gè)表單是否有效踢匣,可以在表單自身調(diào)用 checkValidity() 方法置森。如果所有表單字段都有效,這個(gè)方法返回 true符糊;即使有一個(gè)字段無效,這個(gè)方法也返回false

if (document.form[0].checkValidity()) {
  // 表單有效
} else {
  // 表單無效
}

與 checkValidity() 方法簡單地告訴你字段是否有效相比呛凶,validity 屬性則會(huì)告訴你為什么字段有效或無效男娄。這個(gè)對(duì)象中包含一系列屬性,每個(gè)屬性會(huì)返回一個(gè) 布爾值漾稀。

  • customError:如果設(shè)置了 setCustomValidity()模闲,則返回 true,否則返回 false崭捍。
  • patternMismatch:如果值與指定的pattern 屬性不匹配尸折,返回 true
  • rangeOverflow:如果值比max值大,返回 true
  • rangeUnderflow:如果值比 min 值小殷蛇,則返回 true
  • stepMisMatch:如果 min 和 max 之間的步長值不合理实夹,返回true
  • tooLong:如果值的長度超過了 maxlength 屬性指定的長度,返回 true粒梦。有的瀏覽器(如Firefox4)會(huì)自動(dòng)約束字符數(shù)量亮航,因此這個(gè)值可能永遠(yuǎn)返回 false
  • typeMismatch:如果值不是"mail" 或 "url" 要求的格式,返回 true
  • valid:如果這里的其他屬性都是false匀们,返回true缴淋。cjeckValidity() 也要求相同的值。
  • valueMissing:如果標(biāo)注為 required 的字段中沒有值,則返回 true重抖。

因此露氮,要想得到更具體的信息,就應(yīng)該使用 validity 屬性來檢測(cè)表單的有效性钟沛。

if (input.validity.valueMissing) {
  alert('please specify a value')
} else if (inout.validity.typeMismatch) {
  alert('please enter an email address.')
} else {
  alert('value is invalid')
}
2.4.6畔规、禁用驗(yàn)證

通過設(shè)置 novalidate 屬性,可以告訴表單不進(jìn)行驗(yàn)證讹剔。

<form method="post" action="signup.php" novalidate>

</form>

在 JavaScript中使用 noValidate 屬性可以去的或設(shè)置這個(gè)值油讯,如果這個(gè)屬性存在,值為 true
如果不存在延欠,值為 false陌兑。

document.forms[0].noValidate = true // 禁用表單

如果一個(gè)表單中有多個(gè)提交按鈕,為了指定點(diǎn)擊某個(gè)提交按鈕不必驗(yàn)證表單由捎,可以在相應(yīng)的按鈕上添加 formnovalidate 屬性

<form method="post" action="signup.php">
  <input type="submit" value="submit" />
  <input type="submit" value="Non-validating Submit" formnovalidate />
</form>

三兔综、選擇框腳本

選擇框是通過<select>、<option> 元素創(chuàng)建的狞玛。為了方便與這個(gè)控件交互软驰,除了所有表單字段共有的屬性和方法外,HTMLSelectElement 類型還提供了下列屬性和方法心肪。

  • add(newOption, relOption):向控件中插入 新<option> 元素锭亏,其位置在相關(guān)項(xiàng)(relOption)之前。
  • multiple:布爾值硬鞍,表示是否允許多項(xiàng)選擇慧瘤;等介于HTML中的 multiple 特性
  • options:控件中所有 <option>元素的 HTMLCollection
  • remove(index):移除給定位置的選項(xiàng)
  • selectedIndex:基于0的選項(xiàng)中的索引,如果沒有選中項(xiàng)固该,則值為 -1锅减。對(duì)于支持多選的控件,只保存選中項(xiàng)中第一項(xiàng)的索引值伐坏。
  • size:選擇框中可見的行數(shù)怔匣;等價(jià)于HTML 中的 size特性

選擇框的 type 屬性 不是 "select-one",就是"select-mutiple"桦沉,這取決于 HTML中有沒有 multiple 特性每瞒。選擇框的 value 屬性由當(dāng)前選中項(xiàng)決定,相應(yīng)規(guī)則如下:

  • 如果沒有選中的項(xiàng)纯露,則選擇框的 value 屬性保存空字符串
  • 如果有一個(gè)選中項(xiàng)独泞,而且該項(xiàng)的 value 特性已經(jīng)在 HTML 中指定,則選擇框的 value 屬性等于選中項(xiàng)的 value 特定苔埋。即使 value 特性的值是空字符串懦砂,也同樣遵循此條規(guī)則
  • 如果有一個(gè)選中項(xiàng),但該項(xiàng)的 value 特性在HTML 中未指定,則選擇框的 value 屬性等于該項(xiàng)的文本
  • 如果有多個(gè)選中項(xiàng)荞膘,則選擇框的 value 屬性將依據(jù)前兩條規(guī)則取得第一個(gè)選中項(xiàng)的值罚随。
  <select name="locaation" id="selLocation">
    <option value="Sunnyvale, CA">Sunnyvale</option>
    <option value="">Chine</option>
    <option>Australia</option>
  </select>

如上DOM結(jié)構(gòu):

  • 如果用戶選中了第一項(xiàng)那么值就是 Sunnyvale, CA
  • 如果用戶選中了第二項(xiàng)羽资,則選擇框的值是 空字符串
  • 如果選中第三個(gè)淘菩,則選擇框的值是 Austraila

在DOM中,每個(gè)<option>元素都有一個(gè) HTMLOptionElement 對(duì)象表示屠升,以便于訪問數(shù)據(jù)潮改,HTMLOptionElement對(duì)象添加了下列屬性:

  • index:當(dāng)前選項(xiàng)在 options 集合中的索引
  • label:當(dāng)前選項(xiàng)的標(biāo)簽;等價(jià)于 HTML 中的 label 特性
  • selected:布爾值腹暖,表示當(dāng)前選項(xiàng)是否被選中汇在。將這個(gè)屬性設(shè)置為 true 可以選中當(dāng)前選項(xiàng)
  • text:選項(xiàng)的文本。
  • value:選項(xiàng)的值(等價(jià)于HTML中的value 特性)

大部分屬性的目的脏答,都是為了方便對(duì)選項(xiàng)數(shù)據(jù)的訪問糕殉。

var selectbox = document.forms[0].elements['location']

var text = selectbox.options[0].text
var value = selectbox.options[0].value

選擇框的 change 事件 與其他表單字段的 change 事件觸發(fā)的條件不一樣。其他表單字段的 change 事件是在值被修改且焦點(diǎn)離開當(dāng)前字段時(shí)觸發(fā)殖告,而選擇寬的 change 事件只要選中了選項(xiàng)就會(huì)觸發(fā)阿蝶。

3.1、選擇選項(xiàng)

對(duì)于只允許選擇一項(xiàng)的選擇框黄绩,訪問選中項(xiàng)的最簡單方式羡洁,就是使用選擇框的 selectedIndex 屬性,

var selectedOption = selectbox.options[selectbox.selectedIndex]

獲取后可以像下面這樣顯示該選項(xiàng)的信息

var selectbox = document.forms[0].elements[0]

var selectedIndex = selectbox.selectedIndex
var selectedOption = selectbox.options[selectedIndex]
var selectedValue = selectedOption.value

對(duì)于 可以選擇多項(xiàng)的選擇框爽丹,selectedIndex 屬性就好像只允許選擇一項(xiàng)一樣筑煮。設(shè)置selectedIndex 會(huì)導(dǎo)致取消以前的所有選項(xiàng)并選擇指定的那一項(xiàng),而讀取 selectedIndex 則只會(huì)返回選擇第一項(xiàng)的索引值习劫。

另一種選擇選項(xiàng)的方式,就是去的對(duì)某一項(xiàng)的引用嚼隘,然后將其 selected 屬性設(shè)置為 true诽里。

selectbox.options[0].selected = true

設(shè)置選項(xiàng)的selected 屬性,不會(huì)取消對(duì)其他選中項(xiàng)的選擇飞蛹,因而可以動(dòng)態(tài)選中任意多個(gè)項(xiàng)谤狡。需要注意的是,將selected 屬性設(shè)置為 false對(duì)單選選擇框沒有影響

實(shí)際上卧檐,selected屬性的作用主要是確定用戶選擇了選擇框中的哪一項(xiàng)墓懂。要取得所有選中項(xiàng),可以循環(huán)遍歷選項(xiàng)集合霉囚,然后測(cè)試每個(gè)選項(xiàng)的selected 屬性

function getSelectedOptions(selectbox) {
  var result = new Array()
  var option = null
  
  for(var i = 0, len = selectbox.options.length; i < len; i++) {
    option = selectbox.options[i]
    if (option.selected) {
      return.push(option)
    }
  }
  return result;
}

下面是使用getSelectedOptions() 函數(shù)取得選中項(xiàng)的實(shí)例捕仔。

var selectbox = document.getElementById('selLocation')
var selectedOptions = getSelectedOptions(selectbox)

var message = ''

for(var i = 0, len = selectedOptions.length; i < len; i++) {
  message += 'selectedIndex:' + selectedOptions[i].index + '\nSelected value:' + selectedOptions[i].value
}
console.log(message)

3.2、添加選項(xiàng)

可以使用 JavaScript 動(dòng)態(tài)創(chuàng)建選項(xiàng),并將它們添加到選擇框中榜跌。

第一種方式就是使用如下所示的DOM方法

var newOption = document.createElement('option')
newOption.appendChild(document.createTextNode('option tex'))
newOption.setAttribute('value', 'option value')

selectbox.appendChild(newOption)

第二種方式是使用 Option 構(gòu)造函數(shù)來創(chuàng)建新選項(xiàng)闪唆,構(gòu)造函數(shù)接受兩個(gè)參數(shù):文本(text)、值(value)

var newOption = new Option('Option text', 'Option value')
selectbox.appendChild(newOption) // 在 IE8 及指甲鉗版本中有問題

第三種方式是使用 選擇框的 add() 方法钓葫,接受兩個(gè)參數(shù):要添加的新選項(xiàng)悄蕾、將位于新選項(xiàng)之后的選項(xiàng)。如果想在最后添加一個(gè)選項(xiàng)础浮,第二個(gè)參數(shù)設(shè)置為 null帆调;在IE對(duì) add() 方法的實(shí)現(xiàn)中,第二個(gè)參數(shù)是可選的豆同,如果指定番刊,該參數(shù)必須是新選項(xiàng)之后的選項(xiàng)的索引。兼容DOM的瀏要求必須指定第二個(gè)參數(shù)诱告,因此如果要編寫跨瀏覽器代碼撵枢,需要為第二個(gè)參數(shù)傳入 undefined,即可精居。

var newOption = new Option('option text', 'option value')
selectbox.add(newOPtion, undefined) // 最佳方案

3.3锄禽、移除選項(xiàng)

與添加選項(xiàng)類似,移除選項(xiàng)的方式也有很多種

  • 使用 removeChild() 的方法
selectbox.removeChild(selectbox,options[0]) // 移除第一個(gè)選項(xiàng)
  • 使用選擇框的 remove() 方法靴姿。這個(gè)方法接受一個(gè)參數(shù)沃但,即要移除選項(xiàng)的索引
selectbox.remove(0) // 移除第一個(gè)選項(xiàng)
  • 將相應(yīng)選項(xiàng)設(shè)置為 null。(這種方式也是 DOM 之前瀏覽器的遺留機(jī)制)
selectbox.options[0] = null // 移除第一個(gè)選項(xiàng)

要移除選擇框中所有的項(xiàng)佛吓,可以迭代所有選項(xiàng)并移除它們

function clearSelectbox(selectbox) {
  for (var i = 0, len = selectbox.options.length; i < len; i++) {
    selectbox.remove(0)
  }
}

3.4宵晚、移動(dòng)和重排選項(xiàng)

在DOM標(biāo)準(zhǔn)出現(xiàn)之前,將一個(gè)選擇框中的選項(xiàng)移動(dòng)到另一個(gè)選擇框中是非常麻煩的维雇。使用DOM的appendChild() 方法淤刃,就可以將第一個(gè)選擇框中的選項(xiàng)直接移動(dòng)到第二個(gè)選擇框中。如果為 appendChild() 方法傳入一個(gè)文檔中已有的元素吱型,那么就會(huì)先從該元素的父節(jié)點(diǎn)中移除它逸贾,再把它添加到指定的位置。

var selectbox1 = document.getElementById('selLocations1')
var selectbox2 = document.getElementById('selLocations2')

selectbox2.appendChild(selectbox1.options[0])

移動(dòng)選項(xiàng)與移除選項(xiàng)有一個(gè)共同之處津滞,即會(huì)重置每一個(gè)選項(xiàng)的 index 屬性


重排 選項(xiàng)次序的過程也十分類似铝侵,要將選擇框中的某一項(xiàng)移動(dòng)到特定位置,最合適的 DOM 方法就是 insertBefore()触徐;appendChild() 方法只適用于將選項(xiàng)添加到選擇框的最后咪鲜。
在選擇框中向前移動(dòng)一個(gè)選項(xiàng)的位置

var optionToMove = selectbox.options[1]
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index-1]

將選擇框中的選項(xiàng)向后移動(dòng)一個(gè)位置

selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index+2])

四、序列化表單

隨著 Ajax 的出現(xiàn)撞鹉,表單序列化已經(jīng)成為一種常見需求疟丙。在JavaScript中颖侄,可以利用表單字段的 type 屬性,連同name 和 value 屬性一起實(shí)現(xiàn)對(duì)表單的序列化隆敢。

在表單提交期間发皿,瀏覽器是怎樣將數(shù)據(jù)發(fā)給服務(wù)器的

  • 對(duì)表單字段的名稱和值進(jìn)行 URL 編碼,使用 和號(hào)(&) 分隔拂蝎。
  • 不發(fā)送禁用的表單字段
  • 只發(fā)送勾選的復(fù)選框和單選按鈕
  • 不發(fā)送 type 為 "reset" 和 "button" 的按鈕
  • 多選選擇框中的每一個(gè)選中的值單獨(dú)一個(gè)條目
  • 在單擊按鈕提交表單的情況下穴墅,也會(huì)發(fā)送提交按鈕;否則温自,不發(fā)送提交按鈕玄货。也包括 type 為 "image" 的 <input> 元素
  • <select> 元素的值,就是選中 <option> 元素的 value 特性的值悼泌。如果 <option> 元素沒有 value特性松捉,則是 <option>元素的文本值

在表單序列化過程中,一般不包括任何按鈕字段馆里,因?yàn)榻Y(jié)果字符串很可能是通過其它方式提交的隘世。除此之外的其他上訴規(guī)則都應(yīng)該遵循。

// 表單序列化
function serialize(form) {
  var parts = [],
  field = null,
  i,
  len,
  j,
  optLen,
  option,
  optValue

  for(i = 0, len = form.elements.length; i < len; i++) { // 迭代 表單字段
    field = form.elements[i]

    switch(field.type) {
      case 'select-one':
      case 'select-multiple':
        if (field.name.length) { // 如果有 name 屬性 并且 有值
          for (j = 0, optLen = field.options.length; j < optLen; j++) { // 遍歷 所有 option
            option = field.options[j] 
            if (option.selected) { // 如果當(dāng)前選項(xiàng)被 選擇
              optValue = ''
              if (option.hasAttribute) { // 如果支持 hasAttribute() 方法
                optValue = (option.hasAttribute('value') ? option.value : option.text) // 查找是否存在 value 屬性
              } else { // 不支持 hasAttribute()
                optValue = (option.attributes['value'].specified ? option.value : option.text) // 查明是否已規(guī)定 value 屬性
              }

              parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(optValue))
            }
          }
        }
        break;
      
      case undefined:    // 字段集
      case 'file':       // 文件輸入
      case 'submit':     // 提交按鈕
      case 'reset':      // 重置按鈕
      case 'button':     // 自掉頭按鈕
        break;
      case 'radio':      // 單選按鈕
      case 'checkbox':   // 復(fù)選框
        if (!field.checked) {
          break;
        }

      default: // 默認(rèn)操作
        if (field.name.length) { // 不包含沒有 name 的表單字段
          parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value))
        }
    }
  }

  return parts.join('&')
}

以下是這個(gè) 方法的簡單使用

<form action="www.xxx.xxx" id="myForm">
  地區(qū):<select name="locaation" id="selLocation">
    <option value="Sunnyvale, CA">Sunnyvale</option>
    <option value="Chine">Chine</option>
    <option value="Ireland">Ireland</option>
    <option>Asutralia</option>
  </select>
  <p>用戶名:<input type="text" name="username"></p>
  <p>郵箱:<input type="email" name="email"></p>
  <p><input type="submit" value="submit"></p>
</form>
<script>
var form = document.forms[0]
form.onsubmit = function(event) {
  event.preventDefault()
  var fieldsStr = serialize(form)

  console.log(fieldsStr)
}

</script>

五鸠踪、富文本編輯

富文本編輯丙者,又稱為 WYSIWYG(What You See Is What You Get,所見即所得)营密。在網(wǎng)頁中編輯富文本內(nèi)容械媒,是人本對(duì)web應(yīng)用程序最大的期待之一。這一技術(shù)的本質(zhì)评汰,就是在頁面中嵌入一個(gè)包含空HTML的頁面 iframe纷捞。通過設(shè)置 designMode 屬性,這個(gè)空白的 HTML 頁面可以被編輯被去,而編輯對(duì)象則是該頁面 <body> 元素的 HTML 代碼主儡。designMode 屬性有兩個(gè)可能的值:"off"(默認(rèn)值)和 "on"。在設(shè)置為 "on" 時(shí)惨缆,整個(gè)文檔都會(huì)變得可以編輯

可以給 iframe 指定一個(gè) 非常簡單的HTML 頁面作為其內(nèi)容來源糜值。

<!DOCTYPE>
<html>
  <head>
    <title> Black Page for Rich Text Editing</title>
  </head>
  <body></body>
</html>

在包含頁面中,需要使用 onload 事件處理程序來在恰當(dāng)?shù)臅r(shí)刻設(shè)置 designMode踪央, 如下:

<iframe name="richedit" style="width: 100px; height: 100px;" src="./blank.html"></iframe>

<script>
  window.onload = function() {
    frames['richedit'].document.designMode = 'on'
  }
</script> 

需要注意的是臀玄,不能以文件方式在本地直接用瀏覽器打開的瓢阴,地址欄是file:///畅蹂。可以試著在本地架設(shè)服務(wù)器來調(diào)試

5.1荣恐、使用 contenteditable 屬性

另一種編輯富文本內(nèi)容的方式是使用名為 contenteditable 的特殊屬性液斜,這個(gè)屬性也是由 IE 最早實(shí)現(xiàn)的累贤。可以吧 contenteditable 屬性應(yīng)用給頁面中的任何元素少漆,然后用戶立即就可以編輯該元素臼膏。這種方法之所以受到歡迎,是因?yàn)樗恍枰?iframe示损、空白頁 和 JavaScript 渗磅,只要為元素設(shè)置 contenteditable屬性即可

<div class="editable" id="richedit" contenteditable></div>

如上,元素中包含的任何文本內(nèi)容就都可以編輯了检访,就好像這個(gè)元素變成了 <textarea> 元素一樣始鱼。通過在這個(gè)元素上設(shè)置 contenteditable 元素,也能開打或關(guān)閉編輯模式

var div = document.getElementById('richedit')
div.contentEditable = true 
div.contentEditable = false

contenteditable 屬性有三個(gè)可能的值:"true"表示打開脆贵,"false"表示關(guān)閉医清,"inherit"表示從父元素那里繼承

5.2、操作富文本

與富文本編輯器交互的主要方式卖氨,就是使用 document.execCommand()会烙。這個(gè)方法可以對(duì)文檔指向預(yù)定義的命令,而且可以應(yīng)用大多格式筒捺。這個(gè)方法接受三個(gè)參數(shù):

  • 要執(zhí)行的命令名稱
  • 表示瀏覽器是否應(yīng)該為當(dāng)前命令提供用戶界面的一個(gè)布爾值
  • 指向命令必須的一個(gè)值(如果不需要值柏腻,則傳遞 null)

為了確保跨瀏覽器的兼容性焙矛,第二個(gè)參數(shù)應(yīng)該始終設(shè)置為 false葫盼,因?yàn)?Firefox 會(huì)在改參數(shù)為 true時(shí) 拋出錯(cuò)誤

不同瀏覽器支持的預(yù)定義命令也不一樣。下表列出了那些被支持最多的命令

命令 值(第三個(gè)參數(shù)) 說明
backcolor 顏色字符串 設(shè)置文檔的背景顏色
bold null 將選擇的文本轉(zhuǎn)換為粗體
copy null 將選擇的文本復(fù)制到剪貼板
createlink URL字符串 將選擇的文本轉(zhuǎn)換成一個(gè)鏈接村斟,指向指定的URL
cut null 將選擇的文本剪切到剪貼板
delete null 刪除選擇的文本
fontname 字體名稱 將選擇的文本修改為指定字體
fontsize 1~7 將選擇的文本修改為指定字體大小
forecolor 顏色字符串 將選擇的文本修改為指定的顏色
formatblock 要包圍當(dāng)前文本的HTML標(biāo)簽贫导;如<h1> 使用指定的HTML標(biāo)簽來格式化選擇的文本快
indent null 縮進(jìn)文本
inserthorizontalrule null 在插入字符處插入一個(gè)<hr>元素
insertimage 圖像的URL 在插入字符處插入一個(gè)圖像
insertorderedlist null 在插入字符處插入一個(gè)<ol>元素
insertunorderedlist null 在擦汗如字符處插入一個(gè)<ul>元素
insertparagraph null 在插入字符處插入一個(gè)<p>元素
italic null 將選擇的文本轉(zhuǎn)換成斜體
justifycenter null 將插入光標(biāo)所在文本塊居中對(duì)齊
justifyleft null 將插入光標(biāo)所在文本快左對(duì)齊
outden null 凸排文本(減少縮進(jìn))
paste null 將剪貼板中的文本粘貼到選擇的文本
removeformat null 移除插入光標(biāo)所在文本塊的塊級(jí)格式。這是撤銷 formatblock 命令的操作
selectall null 選擇文檔中的所有文本
underline null 為選擇的文本添加下劃線
unlink null 移除文本的鏈接蟆盹。這是冊(cè)小 createlink的命令的操作

其中孩灯,與剪貼板有關(guān)的命令在不同瀏覽器中的差異極大。Opera 根本沒有實(shí)現(xiàn)任何剪貼板命令逾滥,即使不能通過 document.execCommand() 來執(zhí)行這些命令峰档,但卻可以通過相應(yīng)的快捷鍵來實(shí)現(xiàn)同樣的操作。

使用上訴屬性寨昙,來簡單實(shí)現(xiàn)一個(gè)富文本

  <button id="boldBtn">bold </button>
  <button id="italicBtn">italic</button>
  <button id="linkBtn">a</button>
  <button id="h1Btn">H1</button>
  <iframe name="richedit" style="width: 1000px; height: 300px;display: block" src="blank.html"></iframe>

<script>
window.onload = function() {
    frames['richedit'].document.designMode = 'on'
   
    document.getElementById('boldBtn').onclick = function() {
      frames['richedit'].document.execCommand('bold', false, null)
    }
    document.getElementById('italicBtn').onclick = function() {
      frames['richedit'].document.execCommand('italic', false, null)
    }
    document.getElementById('linkBtn').onclick = function() {
      frames['richedit'].document.execCommand('createlink', false, 'http://www.baidu.com') // 默認(rèn)地址讥巡,簡單模擬
    }
    document.getElementById('h1Btn').onclick = function() {
      frames['richedit'].document.execCommand('formatblock', false, '<h1>')
    }
  }
  
</script>
效果圖

選區(qū)文本后,點(diǎn)擊不同的快捷鍵舔哪,實(shí)現(xiàn)修改外觀


同樣的方法也適用于頁面中 contenteditable 屬性為 "true" 的區(qū)塊欢顷,只要把對(duì)框架的引用替換成當(dāng)前窗口的 document 對(duì)象即可。

<div style="width: 1000px; height: 300px;display: block;border: 1px solid violet" id="div" contenteditable>
</div>

<script>
window.onload = function() {
  document.getElementById('boldBtn').onclick = function() {
    document.execCommand('bold', false, null)
  }
  document.getElementById('italicBtn').onclick = function() {
   document.execCommand('italic', false, null)
  }
  document.getElementById('linkBtn').onclick = function() {
   document.execCommand('createlink', false, 'http://www.baidu.com') // 默認(rèn)地址捉蚤,簡單模擬
  }
  document.getElementById('h1Btn').onclick = function() {
   document.execCommand('formatblock', false, '<h1>')
  }
    
}

需要注意的是抬驴,雖然所有瀏覽器都支持這些命令炼七,但這些命令所產(chǎn)生的 HTML 仍然有很大不同。由于各個(gè)瀏覽器實(shí)現(xiàn)命令的方式不同布持,加上它們通過 innerHTML 實(shí)現(xiàn)轉(zhuǎn)換的方式也不一樣豌拙,因此不能指望富文本編輯器會(huì)產(chǎn)生一致的 HTML。


除了命令之外题暖,還有一些于命令相關(guān)的方法

  • queryCommandEnabled()
    用來檢測(cè)是否可以爭(zhēng)對(duì)當(dāng)前選擇的文本按傅,或者當(dāng)前插入字符所在位置執(zhí)行某個(gè)命令。接受一個(gè)參數(shù)胧卤,即要檢測(cè)的命令逞敷。如果當(dāng)前編輯區(qū)域允許執(zhí)行傳入的命令,這個(gè)方法返回 true灌侣,否則返回 false推捐。
var result = frames['richedit'].document.queryCommandEnabled('bold')

如果能夠?qū)Ξ?dāng)前選擇的文本執(zhí)行"bold"命令,以上代碼就會(huì)返回 ture

  • queryCommandState()
    方法用于確定是否已將指定命令應(yīng)用到了選擇的文本侧啼。
var isBold = frames['richedit'].document.queryCommandState('bold')

如果以前已經(jīng)對(duì)選擇的文本執(zhí)行了"bold"命令牛柒,那么上面的代碼會(huì)返回 ture。

  • queryCommandValue()
    用于取得執(zhí)行命令時(shí)傳入的值(即前面例子中傳給document.execCommand() 第三個(gè)參數(shù)
var fontSize = frames['richedit'].document.queryCommandValue('fontsize')

通過這個(gè)方法可以確定某個(gè)命令是怎樣應(yīng)用到選擇的文本的痊乾,可以據(jù)以確定在對(duì)其應(yīng)用后續(xù)命令是否合適

5.3皮壁、富文本選區(qū)

在富文本編輯器中,適用 框架(iframe)的getSelection() 方法哪审,可以確定實(shí)際選擇的文本蛾魄。這個(gè)方法是 window 對(duì)象和 document對(duì)象的屬性,調(diào)用它會(huì)返回一個(gè)表示當(dāng)前選擇文本的 Selection 對(duì)象湿滓。每個(gè)Selection對(duì)象都有下列屬性滴须。

  • anchorNode:選區(qū)起點(diǎn)所在的節(jié)點(diǎn)。
  • anchorOffset:在到達(dá)選區(qū)起點(diǎn)之前跳過的 anchorNode 中的字符數(shù)量
  • focusNode:選區(qū)終點(diǎn)所在的節(jié)點(diǎn)
  • focusOffset:focusNode中包含在選區(qū)之內(nèi)的字符數(shù)量叽奥。
  • isCollapsed:布爾值扔水,表示選區(qū)的起點(diǎn)和終點(diǎn)是否重合
  • rangeCount:選區(qū)中包含的DOM范圍的數(shù)量

Selection 對(duì)象的這些這些屬性并沒有包含多少有用的信息。下列方法提供了更多信息朝氓,并且支持對(duì)選區(qū)的操作

  • addRange(range):將指定的DOM范圍添加到選區(qū)中
  • collapse(node, offset):將選區(qū)折疊到指定節(jié)點(diǎn)中的相應(yīng)的文本偏移位置魔市。
  • collapseToEnd():將選區(qū)折疊到終點(diǎn)
  • collapseToStart():將選區(qū)折疊到起點(diǎn)位置
  • **containsNode(node):確定指定的節(jié)點(diǎn)是否包含在候選區(qū)中
  • deleteFromDocument():從文檔中刪除選區(qū)中的文本,document.execCommand('delete', false, null) 命令的結(jié)果相同
  • extend(node, offset):將通過 focusNode赵哲、focusOffset 移動(dòng)到指定的位置來擴(kuò)展選區(qū)待德。
  • getRangeAt(index):返回索引對(duì)應(yīng)的選區(qū)中的DOM范圍
  • removeAllRanges():從選區(qū)中移除所有 DOM 范圍。實(shí)際上枫夺,這樣會(huì)移除選區(qū)将宪,因?yàn)檫x區(qū)中至少要有一個(gè)范圍
  • **removeRange(range):從選區(qū)中移除指定的 DOM 范圍
  • selectAllChildren(node):清除選區(qū)并選擇指定節(jié)點(diǎn)的所有子節(jié)點(diǎn)
  • toString():返回選區(qū)所包含的文本內(nèi)容

Selection 對(duì)象的這些方法都極為使用,它們利用了 前面講到的 DOM范圍來管理選區(qū)。由于可以直接操作選擇文本的DOM表現(xiàn)涧偷,因此訪問DOM范圍與 使用 execCommand() 相比,能夠?qū)Ω晃谋揪庉嬈鬟M(jìn)行更加細(xì)化的控制毙死。

    var selection = frames['richedit'].getSelection()
    
    // 取得選擇的文本
    var selectedText = selection.toString()

    // 取得代表選區(qū)的范圍
    var range = selection.getRangeAt(0)

    // 突出顯示選擇的文本
    var span = frames['richedit'].document.createElement('span')
    span.style.backgroundColor = 'yellow'
    range.surroundContents(span)

Firefox 3.6+ 中調(diào)用 document.getSelection() 會(huì)返回一個(gè)字符串燎潮。為此,可以在 Forefox 3.6+ 中該作調(diào)用 window.getSelection(), 從而范圍 selection 對(duì)象扼倘。Firefox 8修復(fù)了這個(gè)問題

IE8 及更早的版本不支持DOM范圍确封,但我們可以通過它支持的 selection 對(duì)象操作選擇的文本。要取得富文本編輯器中選擇的文本再菊,首先必須創(chuàng)建一個(gè)文本范圍爪喘,然后再像下面這樣訪問其 text 屬性。

var range = frames['richedit'].document.selection.createRange()
var selectedText = range.text

雖然使用 IE 的文本范圍來啊執(zhí)行 HTML 操作并不像使用 DOM 范圍那么可靠纠拔,但也不失為一種有效的途徑秉剑。要像前面使用 DOM 范圍那樣實(shí)現(xiàn)相同的文本高亮效果,可以組合使用 **htmlText **屬性和 pasteHTML() 方法

var range = frames['richedit'].document.selection.createRange()
range.pasteHTML('<span style="background-color: yellow">'+ range.htmlText + '</span>');

5.4稠诲、表單與富文本

富文本編輯器中的HTML不會(huì)被自動(dòng)提交給 服務(wù)器侦鹏,而需要我們手工來提交并提交HTML。為此臀叙,通陈运可以添加一個(gè)隱藏的表單字段,讓他的值等于 從 iframe 中提取的 HTML劝萤。

form.onsubmit = function() {
  this.elements['comments'].value = frames['richedit'].document.body.innerHTML
}

對(duì)于 contenteditable 元素渊涝,也可以執(zhí)行類似的操作

form.onsubmit = function() {
  this.elements['comments'].value = document.getElementById('richedit').innerHTML
}

六、小結(jié)

使用 JavaScript 可以增強(qiáng)已有的表單字段床嫌,從而創(chuàng)造出新的功能跨释,或者提升表單的易用性。

  • 可以使用一些標(biāo)準(zhǔn) 或 非標(biāo)準(zhǔn)的方法選擇文本框的全部或部分文本
  • 在文本框的內(nèi)容變化時(shí)厌处,可以通過偵聽鍵盤事件以及檢測(cè)插入的字符煤傍,來允許或禁止用戶輸入
  • 除了Opera 其他主流瀏覽器都允許 通過 JavaScript 訪問 剪貼板中的數(shù)據(jù)。
  • IE允許在任何時(shí)候訪問剪貼板的相關(guān)信息嘱蛋,而其他瀏覽器只能按在發(fā)生剪貼板事件時(shí)才能訪問蚯姆。

富文本編輯功能是通過一個(gè)包含空 HTML 文檔的 iframe 元素來實(shí)現(xiàn)。通過將空文檔的 designMode 屬性設(shè)置為 'on'洒敏,就可以將頁面轉(zhuǎn)換為 可以編輯狀態(tài)龄恋,此時(shí)其表現(xiàn)如同字處理軟件。另外凶伙,也可以將某個(gè)元素設(shè)置為 contenteditable郭毕。在默認(rèn)情況下,可以將字體加粗或者將文本轉(zhuǎn)換為斜體函荣,還可以使用剪貼板显押。
JavaScript通過使用 execCommand() 方法也可以實(shí)現(xiàn)相同的一些功能扳肛。另外,使用 queryCommandEnabled()乘碑、queryCommandState()挖息、queryCommandValue() 方法則可以取得有關(guān)文本選區(qū)的信息。由于以這種方式構(gòu)建的富文本編輯器并不是一個(gè)表單字段兽肤,因此在將其內(nèi)容提交給服務(wù)器之前套腹,必須將 iframe 或 contenteditable 元素中的 HTML 復(fù)制到一個(gè)表單字段中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末资铡,一起剝皮案震驚了整個(gè)濱河市电禀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笤休,老刑警劉巖尖飞,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異店雅,居然都是意外死亡葫松,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門底洗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腋么,“玉大人,你說我怎么就攤上這事亥揖∩豪蓿” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵费变,是天一觀的道長摧扇。 經(jīng)常有香客問我,道長挚歧,這世上最難降的妖魔是什么扛稽? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮滑负,結(jié)果婚禮上在张,老公的妹妹穿的比我還像新娘。我一直安慰自己矮慕,他們只是感情好帮匾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痴鳄,像睡著了一般瘟斜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天螺句,我揣著相機(jī)與錄音虽惭,去河邊找鬼。 笑死蛇尚,一個(gè)胖子當(dāng)著我的面吹牛芽唇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佣蓉,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼亲雪!你這毒婦竟也來了勇凭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤义辕,失蹤者是張志新(化名)和其女友劉穎虾标,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灌砖,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡璧函,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了基显。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蘸吓。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撩幽,靈堂內(nèi)的尸體忽然破棺而出库继,到底是詐尸還是另有隱情,我是刑警寧澤窜醉,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布宪萄,位于F島的核電站,受9級(jí)特大地震影響榨惰,放射性物質(zhì)發(fā)生泄漏拜英。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一琅催、第九天 我趴在偏房一處隱蔽的房頂上張望居凶。 院中可真熱鬧,春花似錦藤抡、人聲如沸排监。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舆床。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挨队,已是汗流浹背谷暮。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盛垦,地道東北人湿弦。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像腾夯,于是被迫代替她去往敵國和親颊埃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容