表單元素之下拉系

發表於2016-01-19

這裡特指下拉框,select。但select有兩種形態,由multiple屬性決定。在多選形態下,使用者按住shift鍵就能實現多選,但用得不多,主要是佔空間。那我們著重說說單選形態及其結構。

下拉框是由多種元素組成,通常我們見過select套著option元素,這中間還能夾一層,optgroup就是對option元素進行分組。option元素裡面不能放置其他元素節點,option元素間除了空白或註釋節點,也不能放其他東西。

optgroup只是裝飾用,對提交資料沒有影響,當我們選中某個option元素時,它的selected屬性就變成true,之前被選中的元素的selected屬性變成false,select元素中selectedIndex的值會變成被選元素的序號(它在所有option元素的位置 )。此外,還有一個鮮為人知的屬性selectedOptions,它是對應一個陣列,裝著被選中的元素,那麼它就換成被選中元素。因此DOM操作是一種非常複雜與高消耗的行為,這導致基於虛擬DOM的react庫的誕生。減少不必要的DOM操作,就能大幅提高效能。

select的值就是被選中的option元素的值,如果使用者定義value屬性,那麼這值就是option.value,否則就是option的innerHTML,也就是option.text。這當中存在相容問題,比如有的瀏覽器會對innerHTML進行兩端空白trim操作,有的不會,建議統一使用trim操作。

option.value的提取方法如下:

我們在看看如何動態新增option元素。這有兩種方式,1是使用W3C的createElement與appendChild,2是使用new Option及options.add方法。

1. 直接使用select.innerHTML

執行發現標準瀏覽器如chrome, firefox執行正常,DOM樹為

IE(678)全家都呵呵了:

原因在於IE使用innerHTML給select賦值時會根據/^&/(尖括號中間的字母、數字,引號,空格)匹配的字元都幹掉,無力吐槽。

2. 使用new Option建立select的options,這是比較推薦的方法。

我們先來看看Option構造器是怎麼用的

除了第一個引數,其他都是可選的,相當於

再看options.add方法。options是select元素的一個陣列屬性,裡面裝著所有option元素。add是其上面的一個方法( IE中它也能出現在select元素上),此方法存在相容問題。

options.add有兩種傳參方式,第一種要來傳入兩個元素,第一個是新option元素,第二個是已有的option元素,新元素會插入到舊元素之前。問題出現在第二個引數預設的情況下:

請注意,在IE6及IE7下請使用不帶null引數的add方法,在FF下請使用帶null引數的方法,IE8下帶不帶都可以。
很奇怪為什麼一定要加null,我猜測add方法裡一定使用了’=== null’來判斷第二的引數或者沒有對引數陣列的長度做驗證。

還有一種傳參方法,第一個是新option元素,第二個是插入位置,不寫預設插入到末尾。

早期IE是不支援傳入兩個元素,只支援傳入元素與插入位置的方式。IE8是兩種方式都支援,對於普通瀏覽器,如果傳入的是索引數值,它不會認為是出錯,還是會新增在最後。

3. 使用document.createElement與appendChild。

這是標準DOM API,基本上無所不能。 在以前的IE4中, document只能建立img, area, option三種元素,到了IE5,一般可以程式設計建立幾乎所以元素, 除了frame和iframe。 而且這些新的建立的元素的屬性都是可讀寫的,並且可以程式設計隨意訪問。但是你必須得先把他們先回到他們相應的集合中或者當前文件中你才能使用, 否則會報錯。

看下一個課題,如何找到可以提交的option元素。因為決定一個option能否提交,除了selected屬性外,還有disabled屬性,由於disabled屬性可能出現在select或optgroup元素上,這問題就複雜了。此外selectedOptions陣列屬性並不可靠,不是所有瀏覽器都支援。jQuery在處理這裡也花了不少程式碼。

option還有兩個重要的屬性,index是返回當前option元素在此select下所有option元素的位置。label是顯示其文字,行為有點像text,優化級比text高,但有點相容性問題。

通常情況下,IE,opera,safari是顯示Label1與Label2,而chrome, firefox(即使是4.01的版本)是顯示TextContent1與TextContent2,這個古老的bug(見這裡) 至今沒修復。

相關文章