# 节点层级

document 节点表示每个文档的根节点,根节点的唯一子节点是<html>元素,我们称之为文档元素。DOM 中总共有 12 种节点类型,这些类型都继承一种基本类型

# Node 类型

每个节点都有 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 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)
  1. nodeName 与 nodeValue 保存着有关节点的信息。对元素而言,nodeName 始终等于元素的标签名,而 nodeValue 则始终为 null。
if (someNode.nodeType == 1){
 value = someNode.nodeName; // 会显示元素的标签名
} 
  1. 节点关系

每个节点都有一个 childNodes 属性,其中包含一个 NodeList 的实例。NodeList 是一个类数组 对象,用于存储可以按位置存取的有序节点。

下面的例子展示了如何使用中括号或使用 item()方法访问 NodeList 中的元素:

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

每个节点都有一个 parentNode 属性,指向其 DOM 树中的父元素。

previousSibling 和 nextSibling 可以在这个列表的节点间导航。这个列表中第一个节点的previousSibling 属性是 null,最后一个节点的nextSibling 属性也是 null

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.");
} 

firstChild 和 lastChild 分别指向childNodes 中的第一个和最后一个子节点。

节点关系图 节点关系图

  1. 操纵节点

appendChild(),用于在 childNodes 列表末尾添加节点,返回新添加的节点。如果把文档中已经存在的节点传给 appendChild(),则这个节点会从之前的位置被转移到新位置。

insertBefore()方法接收两个参数:要插入的节点和参照节点。调用这个方法后,要插入的节点会变成参照节点的 前一个同胞节点,并被返回。如果参照节点是 null,则 insertBefore()与 appendChild()效果相同。

appendChild() 和 insertBefore() 在插入节点时不会删除任何已有节点。

replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。

let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);

removeChild()方法移除节点。这个方法接收一个参数,即要移除的节点。返回被移除的节点


  1. 其他方法 cloneNode()会返回与调用它的节点一模一样的节点。cloneNode()方法接收一个布尔值参数,表示是否深复制。

cloneNode()方法不会复制添加到 DOM 节点的 JavaScript 属性,比如事件处理程序。这个方法只复制 HTML 属性,以及可选地复制子节点。除此之外则一概不会复制。IE 在很长时间内会复制事件处理程序,这是一个 bug,所以推荐在复制前先删除事件处 理程序。

normalize()是处理文档子树中的文本节点,在节点上调用 normalize()方法会检测这个节点的所有后代,从中搜索上述两种情形。如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。

# Document 类型

  1. 文档子节点 document对象的documentElement 属性,指向 HTML 页面中的<html>元素

document 对象的body 属性,直接指向<body>元素

  1. 文档信息

title属性是包含<title>元素中的文本,修改 title 属性并不会改变<title>元素。

URL属性包含当前页面的完整URL

domain包含页面的域名

referrer属性包含链接到当前页面的那个页面的 URL。如果当前页面没有来源,则 referrer 属性包含空字符串

这些属性中只有 domain 属性是可以设置的

domain 属性设置的值是有限制的,如果 URL包含子域名如 p2p.wrox.com,则可以将 domain 设置为"wrox.com"(URL包含“www”时也一样,比如 www.wrox.com)。不能给这个属性设置 URL 中不包含的值,比如:

// 页面来自 p2p.wrox.com
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错!

浏览器对 domain 属性还有一个限制,即这个属性一旦放松就不能再收紧。比如,把document.domain 设置为"wrox.com"之后,就不能再将其设置回"p2p.wrox.com",后者会导致错误,比如:

// 页面来自 p2p.wrox.com
document.domain = "wrox.com"; // 放松,成功
document.domain = "p2p.wrox.com"; // 收紧,错误!
  1. 定位元素

document.getElementById()通过id获取元素

document.getElementsByTagName()通过元素的标签名返回包含零个或多个元素的 NodeList

document.getElementsByName()获取给定 name 属性的所有元素

  1. 特殊集合

document.links 包含文档中所有带 href 属性的<a>元素。

document.forms 包含文档中所有<form>元素。

document.links 包含文档中所有带 href 属性的<a>元素。

document.images 包含文档中所有<img>元素

  1. DOM 兼容性检测

hasFeature改方法已废弃

let hasXmlDom = document.implementation.hasFeature("XML", "1.0"); 
  1. 文档写入 write()和 writeln()方法都接收一个字符串参数,可以将这个字符串写入网页中。write()简单地写入文本,而 writeln()还会在字符串末尾追加一个换行符(\n)。这两个方法可以用来在页面加载期间向页面中动态添加内容。如果是在页面加载完之后再调用 document.write(),则输出的内容会重写整个页面

open()和 close()方法分别用于打开和关闭网页输出流。

# Element 类型

nodeName 或 tagName 属性来获取元素的标签名,返回的是大写的标签名

# Text 类型

# Comment 类型

# CDATASection 类型

# DocumentType 类型

# DocumentFragment 类型

# Attr 类型

# DOM编程

# 动态脚本

# 动态样式

# 操作表格

# 使用 NodeList

# MutationObserver 接口

使用 MutationObserver 可以观察整个文档、DOM 树的一部分,或某个元素。此外还可以观察元素属性、子节点、文本,或者前三者任意组合的变化。

注意

新引进 MutationObserver 接口是为了取代废弃的 MutationEvent。

# 基本用法

  1. observe()

新创建的 MutationObserver 实例不会关联 DOM 的任何部分,需要用observe方法建立与DOM的联系。

observe()方法接收两个必需的参数:要观察其变化的 DOM 节点,以及一个 MutationObserverInit 对象。

let observer = new MutationObserver(() => console.log('<body> attributes changed'));
observer.observe(document.body, { attributes: true });
document.body.className = 'foo';
console.log('Changed body class'); 
// Changed body class
// <body> attributes changed 
  1. 回调与 MutationRecord

  2. disconnect()方法

要提前终止执行回调,可以调用 disconnect()方法

  1. 复用 MutationObserver

  2. 重用 MutationObserver

调用 disconnect()并不会结束 MutationObserver 的生命。还可以重新使用这个观察者,再将它关联到新的目标节点。

let observer = new MutationObserver(() => console.log('<body> attributeschanged'));
observer.observe(document.body, { attributes: true });
// 这行代码会触发变化事件
document.body.setAttribute('foo', 'bar');
setTimeout(() => {
 observer.disconnect();
 // 这行代码不会触发变化事件
 document.body.setAttribute('bar', 'baz');
}, 0);
setTimeout(() => {
 // Reattach
 observer.observe(document.body, { attributes: true });
 // 这行代码会触发变化事件
 document.body.setAttribute('baz', 'qux');
}, 0);
// <body> attributes changed
// <body> attributes changed 

# MutationObserverInit 与观察范围

# 异步回调与记录队列

# 性能、内存与垃圾回收