HTML DOM学习笔记:Node类型

Node类型

除了IE(该死的IE),其他所有浏览器都可以访问到Node类型,而JS中所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法.
每个节点都有一个nodeType属性,可以表明节点的类型,我们来看看有哪些类型吧

  1. Node.ELEMENT_NODE(1)
  2. Node.ATTRIBUTE_NODE(2)
  3. Node.TEXT_NODE(3)
  4. Node.CDATA_SECTION_NODE(4)
  5. Node.ENTITY_REFERENCE_NODE(5)
  6. Node.ENTITY_NODE(6)
  7. Node.PROCESSING_INSTRUCTION_NODE(7)
  8. Node.COMMENT_NODE(8)
  9. Node.DOCUMENT_NODE(9)
  10. Node.DOCUMENT_TYPE_NODE(10)
  11. Node.DOCUMENT_FRAGMENT_NODE(11)
  12. Node.NOTATION_NODE(12) nodeNamenodeValue属性则完全取决于nodeType,对于元素节点,nodeName保存的始终为标签名,而nodeValue保存的值始终为null

childNodes属性中保存着一个NodeList对象(类数组对象,并不是Array的实例),NodeList是动态的,是基于DOM结构动态执行查询的结果,我们能通过以下方式来访问子节点.

1
2
3
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;

对于NodeList对象,我们还可以转换为数组,如下

1
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);

值得注意,该段代码在IE8及更早版本前是报错的,这是因为IE8及更早版本将NodeList实现为一个COM对象,而我们不能像使用JScript对象那样使用该对象,所以上述代码会导致错误.以下是对于兼容性的解决方法.

1
2
3
4
5
6
7
8
9
10
11
12
function convertToArray(nodes){
var array = null;
try{
array = Array.prototype.slice.call(nodes,0);//非IE
}catch(ex){
array = new Array();
for(var i = 0,len = nodes.length;i < len;i++){
array.push(node[i]);
}
}
return array;
}

每个节点还有parentNode属性,该属性指向文档树中的父节点.包含在childNodes列表中的所有节点都有相同的父节点,而childNodes列表中的每个节点之间为同胞节点,可以通过previousSiblingnextSibling属性访问.如果列表只有一个节点,则该节点上述两个属性为null.
父节点与其第一个和最后一个子节点之间也存在特殊关系.父节点存在属性firstChildlastChild分别指向它们.其中firstChildchildNodes[0]相等,而x.lastChildx.childNodes[x.childNodes.length-1]相等.

节点中hasChildNodes()方法也是一个有用的方法,在包含节点情况下返回true,否则返回false.

所有节点最后一个属性都为ownerDocument,它指向表示整个文档的文档节点,即#document,可以使我们直接到达顶层.

操作节点

注意:关系指针都是只读的,所以DOM提供了一些函数供我们操作节点.
以下介绍了四种操作方法,都需要先取得父节点,但是需要明白不是所有类型的节点都有/支持子节点.

插入节点

1
appendChild(node) //向childNodes列表末尾添加一个节点,并在添加后,关系指针会相应更新.

值得注意的是appendChild()添加的节点如果已经是文档的一部分,那么相当于转移节点.

1
insertBefore(node,null) //参数一为插入节点,第二个参数则为参照节点,设为null则效果与appendChild一样,存在参照节点则插入节点变成参照节点前一个同胞节点(previousSibling)

移除/替换节点

1
2
removeChild(oldNode) //移除并非替换节点,返回值为oldNode
replaceChild(newNode,oldNode) //oldNode替换为newNode,oldNode虽然技术上仍然存在,但是文档中没有了它的位置

其他方法

这里有两个方法是所有类型节点都有的,它们就是cloneNode(boolean)normalize(),我们重点介绍cloneNode()

1
cloneNode(boolean) //传入一个布尔值,该值决定执行深复制(节点及整个节点树)还是浅复制(仅复制本身)

注意点一:深复制IE9之前版本不会为空白符创建节点,所以深复制时childNodes长度会有所不同.
注意点二:cloneNode()方法只复制特性和子节点(指定情况下),不会复制JS属性如事件处理程序等等(除开IE,存在复制事件处理程序的bug),所以复制前最好先移除事件处理程序.