DOM

DOM(Document Object Model): 是针对HTML和XML文档的一个API(Application Program Interface)。
DOM描绘了一个层次化的节点树,允许开发人员添加,移除,修改页面的一部分。
换言之,DOM 定义了所有 HTML 元素的对象和属性,以及访问它们的方法。

文档节点是每个文档的根节点,文档节点只有一个子节点(例如<html>)。
<html>元素又成为文档元素,每个文档只能有一个文档元素。

DOM节点

HTML 文档中的所有内容都是节点。共有12种节点类型。

  • 整个文档是一个文档节点
  • 每个 HTML 元素是元素节点
  • HTML 元素内的文本是文本节点
  • 每个 HTML 属性是属性节点
  • 注释是注释节点

Node类型

DOM1级定义了Node接口,在js中作为Node类型实现(除IE不能访问这个类型)。
js中所有节点都继承这个类型,所有节点类型都共享着相同的基本属性和方法。

12种节点类型,用12个数值常亮表示。

  • Node.ELEMENT_NODE (1);
  • Node.ATTRIBUTE_NODE (2);
  • Node.TEXT_NODE (3);
  • Node.CDATA_SECTION_NODE (4);
  • Node.ENTITY_REFERENCE_NODE (5);
  • Node.ENTITY_NODE (6);
  • Node.PROCESSING_INSTRUCTION_NODE (7);
  • Node.COMMENT_NODE (8);
  • Node.DOCUMENT_NODE (9);
  • Node.DOCUMENT_TYPE_NODE (10);
  • Node.DOCUMENT_FRAGMENT_NODE (11);
  • Node.NOTATION_NODE (12)
// 获取ELEMENT_NODE节点类型值(IE中无效,IE 没有公开 Node 类型的构造函数)
Node.ELEMENT_NODE

// 通过节点的nodeType属性获取节点类型
document.nodeType
9

// nodeType保存的是元素的标签名
document.nodeName
"#document"

// nodeValue
document.nodeValue
null

节点关系

节点关系图

关系指针都是只读的。

childNodes
每个节点都有一个childNodes属性,保存着NodeList对象。
NodeList并不是Array的实例,他保存着一组有序的节点,也有length属性(值为保存的节点的个数)。
NodeList是基于DOM结构动态执行查询的结果,DOM结果变化能够自动反应到NodeList对象中。

// 访问NodeList对象
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;

// 将NodeList转换为数组
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
// 或
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(nodes[i]);
        }
    }
    return array;
}

parentNode

指向文档树中的父节点

previousSibling/nextSibling

previousSibling:访问同一个列表中的前一个节点
nextSibling:访问同一个列表中的后一个节点

if (someNode.nextSibling === null){  // 最后一个节点
    alert("Last node in the parent’s childNodes list.");
} else if (someNode.previousSibling === null){  // 第一个节点
    alert("First node in the parent’s childNodes list.");
}

if (someNode.nextSibling === someNode.previousSibling) {
    // 判断列表只有一个节点
}

hasChildNodes

该节点是否包含子节点

ownerDocument

所有节点都有ownerDocument属性,指向整个文档的文档节点,表示任何节点都属于它所在的文档。
任何节点不能同时存在于俩个或更多文档。可以直接访问文档节点。 document.ownerDocument null

操作节点

将DOM树看成是由一系列指针链接起来。任何DOM节点不能同时出现在文档中的多个位置。

替换和移除的节点依然为文档所有,只是没有自己的位置了。
以下四种方法必须在取得父节点情况下使用。
在不支持子节点的节点使用下面四种方法会报错。

appendChild()

  1. 当添加的节点为新节点
    向 childNodes 列表的末尾添加一个节点。
  2. 添加的节点是文章的一部分
    将该节点从原来的位置转移到最后一个节点位置。
var returnedNode = someNode.appendChild(newNode); 返回添加的节点
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //true

insertBefore()

把节点放在 childNodes 列表中某个特定的位置上. 这个方法接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插 入的节点会变成参照节点的前一个同胞节点( previousSibling ),同时被方法返回。

//插入后成为最后一个子节点
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); //true

//插入后成为第一个子节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode);//true
alert(newNode == someNode.firstChild); //true

//插入到最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //true

replaceChild()

替换指定节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置。 该节点的所有关系指针都会从被它替换的节点复制过来。

接受两个参数:要插入的节点和要替换的节点

//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild); // 返回被替换的节点

//替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);

removeChild()

移除节点。

//移除第一个子节点
var formerFirstChild = someNode.removeChild(someNode.firstChild);  // 返回被移除的节点

//移除最后一个子节点
var formerLastChild = someNode.removeChild(someNode.lastChild);

其他方法

cloneNode()

对调用这个方法的节点创建一个完全相同的副本。复制后返回的节点副本属于文档所有, 但并没有为它指定父节点。通过 appendChild() 、 insertBefore() 或 replaceChild() 将它 添加到文档中。

接受布尔值参数:
true:执行深复制,也就是复制节点及其整个子节点树
false:执行浅复制,即只复制节点本身

<ul>
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
</ul>

var deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); //3(IE < 9)或 7(其他浏览器)

var shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); //0

cloneNode() 方法不会复制添加到 DOM 节点中的 JavaScript 属性,例如事件处 理程序等。这个方法只复制特性、(在明确指定的情况下也复制)子节点,其他一切 都不会复制。IE 在此存在一个 bug,即它会复制事件处理程序,所以我们建议在复制 之前最好先移除事件处理程序。

normalize()

处理文档树中的文本节点.
由于解析器的实现或 DOM 操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点 的情况。如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点。