JS的動態合集
前言
DOM是JavaScript重要組成部分,在DOM中有三個特別的集合分別是NodeList(節點的集合),NamedNodeMap(元素屬性的集合)和HTMLCollection(html元素的集合)。這三個集合有一些共同的特點:它們都是一個類數組對象,可以通過中括號表達式來訪問集合中元素,也有length屬性。但它們並不是數組,而且它們都是動態的(querySelectorAll()返回的NodeList除外),會根據頁面元素的變化而變化。下面就來介紹一下它們。
NodeList集合
如何獲取NodeList集合
DOM將HTML頁面解析成一個由多層次節點構成的結構。節點是頁面結構的基礎,而所有節點繼承自Node類型,因此所有節點共享着基本的屬性和方法。Node類型有一個childNodes屬性,所以所有節點都擁有這個屬性。而通過這個屬性就可以得到一個保存着本節點的子節點組成的NodeList對象。
如何操作NodeList
NodeList可以通過方括號表達式來訪問,也可以通過item()方法來訪問,也有length屬性,可以訪問元素個數。 雖然javascript中的數組可以修改length屬性。但NodeList集合並不是數組,而且它是頁面一片區域的DOM結構映射。所以請不要修改NodeList對象的length值。
var firstChild = someNode.childNodes[0];//獲取第一個元素 var secondChild = someNode.childNodes.item(1);//獲取第二個元素 var count = someNode.childNodes.length;//獲取集合長度
NamedNodeMap
如何獲取NamedNodeMap
Element類型這種DOM節點是唯一擁有attributes屬性的一種節點類型。而attribute屬性中就包含NamedNodeMap集合。它由元素的特性組成。所以只要是元素節點,調用attributes屬性就可以得到NamedNodeMap集合。
如何操作NamedNodeMap集合
可能一些人沒有聽過NamedNodeMap對象,該對象的常見實例對象是attributes屬性
<div id="test"></div> <script> var attrs = test.attributes; console.log(attrs instanceof NamedNodeMap); //true </script>
NamedNodeMap集合的元素擁有nodeName和nodeValue屬性,分別表示元素特性名稱和特性值,也擁有以下方法:
- getNamedItem(name);返回nodeName屬性等於name的節點;
- removeNamedItem(name);從列表中移除nodeName節點;
- setNamedItem(node);向列表中添加節點,以節點nodeName為屬性索引;
- item(pos);返回位於數字pos位置的節點。
var className = element.attributes.getNamedItem('class').nodeValue; var className1 = element.attributes['class'].nodeValue; var oldAttr = element.attributes.removeNamedItem('class');
一般元素節點的getAttribute()、removeAttribute()和setAttribute()方法都可以完成以上操作
HTMLCollection
如何得到HTMLCollection集合
HTMLCollection是元素節點的集合可以通過getElementsByTagName()方法(獲取同類型的元素)、getElementsByClassName()(獲取class特性相同的元素)、getElementsByName()方法(獲取name特性相同的元素)、document的anchors屬性(包含name特性的a元素),forms屬性(包含文檔所有form元素),images屬性(包含文檔所有img元素),links屬性(文檔所有帶href特性的a元素)。
與NodeList區別
NodeList集合主要是Node節點的集合,而HTMLCollection集合主要是Element元素節點的集合。Node節點共有12種,Element元素節點只是其中一種。
注意事項
這三個集合都是“動態的”;當文檔結構發生變化時,它們都會更新,所以以下操作就會發生錯誤:
var divs = document.getElementsByTagName('div'); for (var i = 0; i < divs.length; i++) { var div = document.createElement('div'); document.body.appendChild(div); }
以上代碼中的for循環將會一直執行,因為div得到的HTMLCollection集合是動態的,當每次給body添加元素divs都會進行更新,所以會一直循環下去。
解決方法1:將初始的集合的length存起來
var divs = document.getElementsByTagName('div'); for (var i = 0, var len = divs.length; i < len; i++) { var div = document.createElement('div'); document.body.appendChild(div); }
解決方法2:將以上集合轉化為數組
var divs = document.getElementsByTagName('div'); //將HTMLCollection轉換為數組(此方法在IE8及以前不能用) var divsArr = Array.prototype.slice.call(divs,0); for (var i = 0; i < divsArr.length; i++) { var div = document.createElement('div'); document.body.appendChild(div); }
由於IE8-瀏覽器將NodeList實現為一個COM對象,不能使用Array.prototype.slice()方法,必須手動枚舉所有成員
將以上集合轉為數組的方法(通用):
function convertListToArray(nodes) { var array = null; try{//標准瀏覽器 array = Array.prototype.slice.call(nodes,0); }catch(ex){//IE8及以下遍歷每一項分別添加到數組中 array = new Array(); for(var i = 0,len = nodes.length;i < len;i++){ array.push(nodes[i]); } } return array; }
JS的靜態合集
前言
除了getElementsByClassName()方法外,HTML5中DOM拓展了querySelectorAll()、querySelector()和matchesSelector()這3種方法,通過CSS選擇符查詢DOM文檔取得元素的引用的功能變成了原生的API,解析和樹查詢操作在瀏覽器內部通過編譯后的代碼來完成,極大地改善了性能。
與getElementById()方法不同,querySelector()方法得到的對象是靜態對象
與getElementsByTagName()等方法不同,querySelectorAll()方法得到的類數組對象是靜態合集。
querySelector()
querySelector()方法接收一個CSS選擇符,返回與該模式匹配的第一個元素,如果沒有找到匹配的元素,返回null。該方法既可用於文檔document類型,也可用於元素element類型。
<body style="height: 100%;"> <div class="box" id="box" style="height: 200px;"> <ul class="list" style="height:100px"> <li class="in" style="height: 30px;">1</li> <li class="in" style="height: 30px;" title="test">2</li> <li class="in" style="height: 30px;">3</li> </ul> </div> <script> //因為沒有.null類名,所以返回null var oNull = document.querySelector('.null'); console.log(oNull); //null //通過<body>標簽取得元素 var body = document.querySelector("body"); console.log(body.style.height); //100% //通過id屬性取得元素 var oBox = document.querySelector('#box'); console.log(oBox.style.height); //200px //通過結合元素的類選擇器取得元素 var oList = document.querySelector('ul.list'); console.log(oList.style.height); //100px //通過類名取得元素 var oIn = document.querySelector('.in'); console.log(oIn.innerHTML); //1 //通過屬性選擇器取得元素 var oTest = body.querySelector('[title="test"]'); console.log(oTest.innerHTML); //2 </script> </body>
querySelectorAll()
querySelectorAll()接收一個CSS選擇符,返回一個類數組對象NodeList的實例。具體來說,返回的值實際上是帶有所有屬性和方法的NodeList,而其底層實現則類似於一組元素的快照,而非不斷對文檔進行搜索的動態查詢。這樣實現可以避免使用NodeList對象通常會引起的大多數性能問題。
沒有匹配元素時,返回空的類數組對象,而不是null
<body style="height: 100%;"> <div class="box" id="box" style="height: 200px;"> <ul class="list" style="height:100px"> <li class="in" style="height: 30px;">1</li> <li class="in" style="height: 30px;" title="test">2</li> <li class="in" style="height: 30px;">3</li> </ul> </div> <script> //返回[] var oNull = document.querySelectorAll('.null'); console.log(oNull); //取得body元素 var body = document.querySelectorAll("body")[0]; console.log(body.style.height); //100% //取得所有class為"in"的元素 var oIn = document.querySelectorAll('.in'); for (var i = 0; i < oIn.length; i++) { console.log(oIn[i].innerHTML); //1,2,3 } //取得title屬性為test的元素 var oTest = body.querySelectorAll('[title="test"]'); console.log(oTest); //[li.in] </script> </body>
缺陷
selector類方法在元素上調用時,指定的選擇器仍然在整個文檔中進行匹配,然后過濾出結果集,以便它只包含指定元素的后代元素。它意味着選擇器字符串能包含元素的祖先而不僅僅是所匹配的元素。
<div id="container"> <div>1</div> <div>2</div> </div> <script> var container = document.getElementById('container'); console.log(container.querySelectorAll('div div')); //[div div] </script>
所以,如果出現后代選擇器,為了防止該問題,可以在參數中顯式地添加當前元素的選擇器
<div id="container"> <div>1</div> <div>2</div> </div> <script> var container = document.getElementById('container'); console.log(container.querySelectorAll('#container div div')); //[] console.log(container.querySelectorAll('#container div')); //[div div] console.log(container.querySelectorAll('div')); //[div div] </script>