[TOC]
四、动态集合NodeLIst-HTMLCollection等
1. NodeList
NodeList 对象是一个节点的集合,是由 Node.childNodes 和 document.querySelectorAl返回的.
属性
length: NodeList 对象中包含的节点个数.
方法
item ( idx ):返回NodeList对象中指定索引的节点,如果索引越界,则
返回null.等价的写法是nodeList[idx], 不过这种情况下越界访问将返回undefined.NodeList.prototype.forEach():
forEach方法用于遍历 NodeList 的所有成员。它接受一个回调函数作为参数,每一轮遍历就执行一次这个回调函数,用法与数组实例的forEach方法完全一致。NodeList.prototype.keys(),NodeList.prototype.values(),NodeList.prototype.entries():这三个方法都返回一个 ES6 的遍历器对象,可以通过
for...of循环遍历获取每一个成员的信息。区别在于,keys()返回键名的遍历器,values()返回键值的遍历器,entries()返回的遍历器同时包含键名和键值的信息。
<div id="one"><span>1</span></div>
<script>
childNodes = one.childNodes
console.log(childNodes.length) // 1
console.log(childNodes.item(0)) // <span>1</span>
console.log(childNodes[0]) // <span>1</span>
// 回调函数f的三个参数依次是当前成员、位置和当前 NodeList 实例。
// forEach方法的第二个参数,用于绑定回调函数内部的this,该参数可省略。
childNodes.forEach(function f(item, i, list) {
// ...
}, this);
for (var key of childNodes.keys()) {
console.log(key);
}
// 0
for (var value of childNodes.values()) {
console.log(value);
}
// <span>1</span>
for (var entry of childNodes.entries()) {
console.log(entry);
}
// (2) [0, span]
</script>
1.1 Node.childNodes返回动态集合
Node.childNodes返回的NodeList对象是个实时集合。也就是说文档中的节点树发生变化,则已经存在的 NodeList 对象也会变化。
下面的例子中,在添加一个节点后,NodeList 对象也实时的增加了:
<div id="one"><span>1</span></div>
<script>
var child_nodes = one.childNodes;
console.log(child_nodes.length); // 1
one.appendChild(document.createElement('div'));
console.log(child_nodes.length); // 2
</script>
1.2 document.querySelectorAll()返回静态集合
document.querySelectorAll()返回的NodeList对象是个静态的。也就是说文档中节点树发生变化,已存在的NodeList 对象不会变化。
下面的例子中添加了一个节点,但是NodeList 对象没有变化:
<div class="one"><span>1</span></div>
<script>
var one = document.querySelector(".one")
var child_nodes = one.querySelectorAll("span");// NodeList [span]
console.log(child_nodes.length) // 1
one.appendChild(document.createElement('span')); // 添加了一个节点
console.log(child_nodes.length); // 1
console.log(one.querySelectorAll("span").length); // 2
</script>
1.3 NodeList是类数组对象
NodeList是类数组对象,不是数组,除了forEach方法,NodeList 没有这些类似数组的方法。
注意:forEach方法有的浏览器不支持(QQ浏览器老版本、IE8以前)。
JavaScript 的继承机制是基于原型的。数组元素之所以有一些数组方法(比如forEach和 map),是因为它的原型链上有这些方法,如下:
myArray --> Array.prototype --> Object.prototype --> null (想要获取一个对象的原型链,可以连续的调用 Object.getPrototypeOf,直到原型链尽头).
forEach, map这些方式其实是Array.prototype 这个对象的方法。
和数组不一样,NodeList的原型链是这样的:
myNodeList --> NodeList.prototype --> Object.prototype --> null
NodeList的原型上除了类似数组的forEach方法之外,还有item,entries,keys和values方法。
1.4 遍历NodeList对象方法
将NodeList对象转为数组
var div_list = document.querySelectorAll('div'); // 返回 NodeList var div_array = Array.prototype.slice.call(div_list); // 将 NodeList 转换为数组 //ES6 - Array.from(); var div_array_from = Array.from(div_list); //将 NodeList 转换为数组由于IE8-浏览器将NodeList实现为一个COM对象,不能使用Array.prototype.slice()方法,必须手动枚举所有成员
直接使用Array的forEach方法
var forEach = Array.prototype.forEach; var divs = document.getElementsByTagName( 'div' ); var firstDiv = divs[ 0 ]; forEach.call(firstDiv.childNodes, function( divChild ){ divChild.parentNode.style.color = '#0F0'; });请注意,在上面的代码中,将某个宿主对象 (如
NodeList) 作为this传递给原生方法 (如 forEach) 不能保证在所有浏览器中工作,已知在一些浏览器中会失败。使用for循环
for (var i = 0; i < myNodeList.length; ++i) { var item = myNodeList[i]; // 调用 myNodeList.item(i) 是没有必要的 }
2. HTMLCollection
HTMLCollection 接口表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合(generic collection),还提供了用来从该集合中选择元素的方法和属性。
属性
length: 只读,返回集合当中子元素的数目。
方法
item ( idx ):
根据给定的索引(从0开始),返回具体的节点。如果索引超出了范围,则返回
null。namedItem():
根据 Id 返回指定节点,或者作为备用,根据字符串所表示的
name属性来匹配。根据 name 匹配只能作为最后的依赖,并且只有当被引用的元素支持name属性时才能被匹配。如果不存在符合给定 name 的节点,则返回null。
<input type="text" name="mac" id="mm">
<script>
cc = document.getElementsByTagName("input");
// HTMLCollection [input#mm, mm: input#mm, mac: input#mm]
cc.namedItem("mac") // <input type="text" name="mac" id="mm">
console.log(cc.namedItem('mm')); // <input type="text" name="mac" id="mm">
console.log(cc.length); // 1
console.log(cc.item(0)) // <input type="text" name="mac" id="mm">
</script>
HTMLCollection对象与NodeList对象类似,也是节点的集合,返回一个类数组对象。
NodeList集合主要是Node节点的集合,而HTMLCollection集合主要是Element元素节点的集合。Node节点共有12种,Element元素节点只是其中一种。
HTMLCollection集合包括getElementsByTagName()、getElementsByClassName()、getElementsByName()等方法的返回值,以及children、document.links、document.forms等元素集合
HTML DOM 中的 HTMLCollection 是即时更新的(live);当其所包含的文档结构发生改变时,它会自动更新。
<div id="test"></div>
<script>
var childN = test.children;
var tags =test.getElementsByTagName('div');
console.log(childN,tags);//[]、[]
test.innerHTML = '<div></div>';
console.log(childN,tags);//[div]、[div]
</script>
[注意]与NodeList对象类似,要想变成真正的数组Array对象,需要使用slice()方法,在IE8-浏览器中,则必须手动枚举所有成员
3. NamedNodeMap
NamedNodeMap 接口表示属性节点 Attr 对象的集合。尽管在 NamedNodeMap 里面的对象可以像数组一样通过索引来访问,但是它和 NodeList 不一样,对象的顺序没有指定。
NamedNodeMap 对象是即时的(live),因此,如果它内部包含的对象发生改变的话,该对象会自动更新到最新的状态。
属性: 该接口没有继承任何属性。
length: 只读, 返回映射(map)中对象的数量。
方法
getNamedItem() : 返回一个给定名字对应的属性节点
setNamedItem(): 替换或添加一个属性节点到映射(map)中。
removeNamedItem(): 移除一个属性节点
item(): 返回指定索引处的属性节点,当索引超出或等于属性节点的数量时,返回
null
<style type="text/css">
.democlass{ color:red; }
</style>
<div id="test" datat-tt="pp">123</div>
<script>
var att = test.attributes;
// NamedNodeMap {0: id, 1: datat-tt, 2: class, id: id,
console.log(att.length); // 2
console.log(att.getNamedItem('id')); // id="test"
var typ=document.createAttribute("class");
typ.nodeValue="democlass";
att.setNamedItem(typ); // 此时页面上字体会变红
att.removeNamedItem('class');// class="democlass" 此时类已经删除了,颜色恢复了
att.item(0); // id="test"
</script>
该对象也是一个动态集合:
<div id="test"></div>
<script>
var attrs = test.attributes;
console.log(attrs);//NamedNodeMap {0: id, length: 1}
test.setAttribute('title','123');
console.log(attrs);//NamedNodeMap {0: id, 1: title, length: 2}
</script>
4. CSSStyleDeclaration
CSSStyleDeclaration 类表示一组 CSS 样式规则。MXML 编译器在和 Flex 应用程序关联的 CSS 文件中为每个选择器自动生成一个 CSSStyleDeclaration 对象。
CSSStyleDeclaration 表示一个CSS属性键值对的集合。它被用于一些API中:
元素节点的
style属性(Element.style)Element.style返回的只是行内样式,并不是该元素的全部样式。通过样式表设置的样式,或者从父元素继承的样式,无法通过这个属性得到。
CSSStyle实例的style属性window.getComputedStyle()的返回值返回的是元素的全部样式
CSSStyleDeclaration 接口可以直接读写 CSS 的样式属性,不过,连词号需要变成骆驼拼写法。
<div></div>
<script>
var divStyle = document.querySelector('div').style;
// CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
divStyle.backgroundColor = 'red';
divStyle.border = '1px solid black';
divStyle.width = '100px';
divStyle.fontSize = '10em';
divStyle.backgroundColor // red
divStyle.border // 1px solid black
divStyle.width // 100px
</script>
上面代码中,style属性的值是一个 CSSStyleDeclaration 实例。这个对象所包含的属性与 CSS 规则一一对应,但是名字需要改写,比如background-color写成backgroundColor。改写的规则是将横杠从 CSS 属性名中去除,然后将横杠后的第一个字母大写。如果 CSS 属性名是 JavaScript 保留字,则规则名之前需要加上字符串css,比如float写成cssFloat。
注意,该对象的属性值都是字符串,设置时必须包括单位,但是不含规则结尾的分号。比如,divStyle.width不能写为100,而要写为100px。
4.1 CSSStyleDeclaration 实例属性
4.1.1 CSSStyleDeclaration.cssText
cssText属性用来读写当前规则的所有样式声明文本
删除一个元素的所有行内样式,最简单方法就是设置cssText的值为空字符串
4.1.2 CSSStyleDeclaration.length
length属性返回一个整数值,表示当前规则包含多少条样式声明。
4.1.3 CSSStyleDeclaration.parentRule
parentRule属性返回当前规则所属的那个样式块(CSSRule 实例)。如果不存在所属的样式块,该属性返回null。
该属性只读,且只在使用 CSSRule 接口时有意义。
<style>.one {font-size: 60px; border: 2px solid;}</style>
<div class="one" style="width: 300px;">one</div>
<script>
// cssText
var one = document.querySelector('.one').style;
one.cssText; // "width: 300px;" 仅显示行内样式
one.cssText += "background-color: red; font-size:20px";// 此时背景色变红
// "width: 300px;background-color: red; font-size:20px"
tt = window.getComputedStyle(document.querySelector('.one'));
tt.cssText; // 会显示所有样式, 但是 只读。
// length
one.length; // 3 "width: 300px;background-color: red; font-size:20px"
tt.length; // 283
// parentRule
var declaration = document.styleSheets[0].rules[0].style;
declaration.parentRule === document.styleSheets[0].rules[0]
// true
</script>
删除一个元素的所有行内样式:
one.cssText = ""; // "" 此时会清空所有行内样式
4.2 CSSStyleDeclaration 实例方法
4.2.1 getPropertyPriority()
getPropertyPriority()方法接受 CSS 样式的属性名作为参数,返回一个字符串,表示有没有设置important优先级。如果有就返回important,否则返回空字符串。
4.2.2 getPropertyValue()
getPropertyValue()方法接受 CSS 样式属性名作为参数,返回一个字符串,表示该属性的属性值。
4.2.3 item()
item()方法接受一个整数值作为参数,返回该位置的 CSS 属性名。
4.2.4 removeProperty()
removeProperty()方法接受一个属性名作为参数,在 CSS 规则里面移除这个属性,返回这个属性原来的值。
4.2.5 setProperty()
setProperty()方法用来设置新的 CSS 属性。该方法没有返回值。
该方法可以接受三个参数:
第一个参数:属性名,该参数是必需的。
第二个参数:属性值,该参数可选。如果省略,则参数值默认为空字符串。在Google Chrome浏览器中,此参数是必需的。
第三个参数:优先级,该参数可选。如果设置,唯一的合法值是
important,表示 CSS 规则里面的!important。
4.2.6 方法使用例子
<div class="one" style="margin: 10px!important; color: red;">one</div>
<script>
// getPropertyPriority()
var one = document.querySelector('.one').style;
// CSSStyleDeclaration {0: "color", 1: "margin-top", 2: "margin-right", 3: "margin-bottom", 4: "margin-left"
one.margin; // "10px"
one.getPropertyPriority('margin'); // "important"
one.getPropertyPriority('color'); // ""
// getPropertyValue()
one.getPropertyValue("margin"); // "10px"
one.getPropertyValue("color"); // "red"
// item()
one.item(0); // "color"
one.item(1); // "margin-top"
// removeProperty()
one.removeProperty("color");// "red", 删除了红色,字体变成了黑色
// setProperty()
one.setProperty('border', '1px solid blue');// 样式会立即生效。
one ; // CSSStyleDeclaration {0: "margin-top", ... 5: "border-right-width", 6: "border-bottom-width"
one.setProperty("width", "300px", "important");// 样式会立即生效
// 注意第三个参数是important, 不是 !important
</script>
5. 遍历动态集合注意事项
动态集合是个很实用的概念,但在使用循环时一定要千万小心。可能会因为忽略集合的动态性,造成死循环
var divs = document.getElementsByTagName("div");
for(var i = 0 ; i < divs.length; i++){
document.body.appendChild(document.createElement("div"));
}
在上面代码中,由于divs是一个HTMLElement集合,divs.length会随着appendChild()方法,而一直增加,于是变成一个死循环
为了避免此情况,一般地,可以写为下面形式
var divs = document.getElementsByTagName("div");
for(var i = 0,len = divs.length; i < len; i++){
document.body.appendChild(document.createElement("div"));
}
一般地,要尽量减少访问NodeList、HTMLCollection、NamedNodeMap的次数。因为每次访问它们,都会运行一次基于文档的查询。所以,可以考虑将它们的值缓存起来