讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議116:推薦使用CSS選擇器 >

建議116:推薦使用CSS選擇器

childNodes、firstChild和nextSibling屬性是不區分元素節點和其他類型節點的,如註釋節點和文本節點(這兩個標籤之間往往只是一些空格)。在許多情況下,只有元素節點會被訪問,所以在循環中,似乎應當對節點返回類型進行檢查,過濾出非元素節點。事實上,這些檢查和過濾都是不必要的DOM操作。

目前許多瀏覽器提供了只返回元素節點的API函數,如果可用最好利用起來,因為寫這些函數比在JavaScript中寫過濾函數要快。

遍歷children比遍歷childNodes更快,因為集合項更少。HTML源碼中的空格實際上是文本節點,它們不包括在children集合中。在所有瀏覽器中,children比childNodes更快,差別不是太大,通常只快1.5~3倍。特別值得注意的是,在IE中,遍歷children明顯快於遍歷childNodes,在IE 6中快24倍,在IE 7中快124倍。

選擇DOM元素經常需要更精細的控制,而不只是採用getElementById和getElementsByTagName_r之類的函數。有時結合這些函數調用并迭代操作它們返回的節點,以獲取所需要的元素,這一精細的過程可能使效率降低。

另外,使用CSS選擇器是一個便捷的確定節點的方法,這是因為大家已經對CSS很熟悉了。許多JavaScript庫為此提供了API,而且最新的瀏覽器提供了一個名為querySelectorAll的原生瀏覽器DOM函數。顯然這種方法比使用JavaScript和DOM迭代並縮小元素列表的方法要快。


var elements=document.querySelectorAll('#menu a');


elements的值將包含一個引用列表,指向那些具有id="menu"屬性的元素。函數querySelectorAll接收一個CSS選擇器字符串參數並返回一個NodeList(由符合條件的節點構成的類數組對像)。此函數不返回HTML集合,這就避免了HTML集合所固有的性能問題,以及潛在的邏輯問題。如果不使用querySelectorAll,達到同樣目標的代碼會冗長一些。


var elements=document.getElementById('menu').getElementsByTagName_r('a');


在這種情況下,elements將是一個HTML集合,要想得到與querySelectorAll同樣的返回值類型,還需要將它複製到一個數組中。

當需要聯合查詢時,使用querySelectorAll更加便利。例如,在頁面中,一些p元素的class名稱是「warning」,另一些class名稱是「notice」,可以用querySelectorAll一次性獲得這兩類節點。


var errs=document.querySelectorAll('p.warning,p.notice');


如果不使用querySelectorAll,那麼獲得同樣列表需要更多工作。一個辦法是選擇所有的p元素,然後通過迭代操作過濾出那些不需要的單元。


var errs=,ps=document.getElementsByTagName_r('p'),classname='';

for(var i=0,len=ps.length;i<len;i++){

classname=ps[i].className;

if(classname==='notice'||classname==='warning'){

errs.push(ps[i]);

}

}


比較上面兩種不同的用法,使用選擇器querySelectorAll比使用getElementsByTagName_r的性能要好很多。因此,如果瀏覽器支持document.querySelectorAll,那麼最好使用它。如果使用JavaScript庫所提供的選擇器API,那麼確認一下該庫是否確實使用了原生方法。如果不是,則需要將庫升級到新版本。

還可以使用另一個函數querySelector獲取節點,它可以返回符合查詢條件的第一個節點。由於querySelectorAll和querySelector這兩個函數都是DOM節點的屬性,所以可以使用document.querySelector('.myclass')來查詢整個文檔中的節點,或者使用elref.querySelector('.myclass')在子樹中進行查詢,其中elref是一個DOM元素的引用。