HTML文档可以看成由各种 DOM 节点组成的文档树,例如:整篇文档是一个文档节点;第个标签都是一个元素节点;文本内容可以看成文本节点;标签属性是属性节点,甚至注释也是注释节点。

nodeType

元素节点的类型可以通过node.nodeType访问,例如:

document.nodeType           # 9

主要的节点类型如下所示:

Element Type Node Type
Element 1
Attribute 2
Text 3
Comment 8
Document 9

除此之外,节点还有一些其它属性,例如nodeName、nodeValue、tagName等。

nodeName

其中nodeName是最有用的属性,它包含的是节点的名称。元素节点的名称是标签名,属性节点的名称是属性名,文本节点的名称是#text,而文档节点的名称是#document。需要注意的是,nodeName包含的节点名字总是大写的。节点的另外一个属性tagName也包含了元素节点的标签名,但它对任何其它类型的节点都不可用,因此实用价值不大。

nodeValue

对于文本节点,nodeValue包含实际的文本内容;对于属性节点,它包含属性的值。而对于元素节点和文档结点则不可用。

寻找 DOM 元素

要开始DOM编程,首先需要找到你所需要进行操作的元素。一般情况下有两种方法可以使用:

  • 通过getElementById或者getElementsByTagName函数。
  • 通过元素的parentNode及其它类似的属性。

其中,前者可以称为长途搜索,后者相对前者可以称为短途搜索。前者可以不管文档的结构而搜索所需要的节点或者节点列表,而后者则是从一个节点至另外一个节点的短距离搜索,它需要了解目标节点是源节点的爷节点还是兄弟节点等。

长途搜索

长途搜索的几个函数如下所示:

document.getElementById()
element.getElementsByTagName()
element.getElementsByName()
element.getElementsByClassName()

其中,第一个只能用于document节点,在整个文档中搜索指定id的节点并返回。后三个可以用于任何HTML元素,分别搜索指定标签名、name属性、class属性的节点并返回一个节点列表。

短途搜索

节点拥有5个属性(parentNode、firstChild、lastChild、nextSibling、previousSibling)和两个节点列表(childNodes[]和children[])来允许你进行短途搜索。当然,并不是每个节点都拥有以上所有的属性,例如文档节点是整个文档的根节点,因此这没有父节点;而文本节点则不能拥有子节点。

nextSibling、previousSibline这对属性分别查找节点的前后兄弟节点,该节点有可能是文本节点。如果要查找非文本兄弟结点,可以使用nextElementSibling和previousElementSibling属性。

childeNodes[]和children[]之间的区别与上面的nextSibling和nextElementSibling的区别相同,前者返回所有子节点,而后者仅返回所有类型为元素节点的子节点。

修改 DOM 文档树

有4个函数来修改文档树,分别是appendChild、insertBefore、removeChild和replaceChild,其中前两个最常用,replaceChild用得相对来说比较少。

一般规则

所有这4个函数返回的都是它们所操作的节点的引用,并且都定义在你想要操作的节点的父节点上。例如,你想要删除节点x,需要这样做:

x.parentNode.removeChild(x)

appendChild

appendChild函数可以添加一个节点并使它成为某个元素的最后一个节点。如果该节点已经存在于文档中,它会从当前位置被移除。该节点的子节点也被一起移动到新位置。

insertBefore

insertBefore函数可以将一个节点插入到其它节点前面,同appendChild一样如果该节点已经存在于文档中,它会从当前位置被移除。该节点的子节点也被一起移动到新位置。

removeChild

removeChild函数可以从文档中移除一个节点以及它的子节点。

replaceChild

replaceChild函数可以把文档中的一个节点替换成另外一个节点。其它同appendChild和insertBefore函数一样。

移除所有的子节点

有时候可能需要将一个节点的所有子节点都移除,有两种方法可以完成这个任务,第一种是使用removeChild函数:

while (x.childNodes[0])
    x.removeChild(x.childNodes[0])

通过简单的while循环,只要仍然存在第一个子节点,就将它从x中删除,直到x没有子节点为止。V

第二种方法就更简单了,只要设置元素的innerHTML属性为空就可以了:

x.innerHTML=""

创建和克隆元素

你可以创建自己的元素和文本节点,并把它们添加到文档树中。也允许你克隆已有的元素,以便你能方便地复制文档的某一个部分以备它用。

createElement和createTextNode

createElement和createTextNode函数正如它们名字所说的那样,分别是创建一个元素节点和文本节点:

var x = document.createElement("p");
var y = document.createTextNode("This is a created element");

x指向新创建的元素节点, y指向新创建的文本节点。目前为止,这些节点并没有添加到文档树中,因此它们还不能正常地呈现在页面上。所需要的就是使用appendChild或者insertBefore函数将它们添加到文档树的某个位置,例如:

x.appendChild(y);
document.body.appendChild(x);

现在,y被添加到x节点并作为它的文本子节点,而x被添加到document.body节点的最后一个位置。在使用createTextNode函数的时候需要注意的是,它并不能创建&copy等HTML实体,而是会原封不动的创建文本节点,遇到这种情况可以考虑使用innerHTML。

cloneNode

cloneNode函数可以克隆一个节点,通过指定参数为true或者false,它可以选择复制或者不复制它的所有子节点。但是一般情况下,参数都会选择true。遗憾的是,cloneNode函数不会复制事件处理程序,因此当你克隆一个节点之后,需要重新定义事件处理程序。

innerHTML

关于innerHTML和其它方法的一个速度比较在这里:Benchmark - W3C DOM vs. innerHTML