《JavaScript高级程序设计》读书笔记
文档对象模型(DOM,Document Object Model)是HTML 和 XML 文档的编程接口。DOM 表示由多层节点构成的文档。
节点层级
HTML 中的每段标记都可以表示为这个树形结构的一个节点。元素节点表示 HTML 元素,属性节点表示属性,文档类型节点表示文档类型,注释节点表示注释。DOM 中总共有12种节点类型,这些类型都继承一种基本类型。
Node 类型
Name | Value |
---|---|
ELEMENT_NODE | 1 |
ATTRIBUTE_NODE | 2 |
TEXT_NODE | 3 |
CDATA_SECTION_NODE | 4 |
PROCESSING_INSTRUCTION_NODE | 7 |
COMMENT_NODE | 8 |
DOCUMENT_NODE | 9 |
DOCUMENT_TYPE_NODE | 10 |
DOCUMENT_FRAGMENT_NODE | 11 |
1. nodeName 与 nodeValue
if(someNode.nodeType == 1) {
value = someNode.nodeName;
}
2. 节点关系
每个节点都有一个childNodes 属性,其中包含一个NodeList 的实例。NodeList 是一个类数组对象,用于存储可以按位置存却的有序节点,是实时的活动对象。
- parentNode 父节点
- childNodes
- previousSibling
- nextSibling
- firstChild 第一个节点
- lastChild 最后一个节点
- hasChildNodes() 如果返回true,则说明节点有一个或多个字节点
- ownerDocument 指向代表整个文档的文档节点的指针
3. 操作节点
- appendChild() 用于在childNodes 列表末尾添加节点。方法返回新添加的节点
- insertBefore() 接收两个参数:要插入的节点和参照节点。要插入的节点会变成参照节点前一个同胞节点
- replaceChild() 接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之
- removeChild() 移除节点。方法返回移除节点。
4. 其他方法
- cloneNode() 接收一个布尔值参数,表示是否深复制。true:会进行深复制,即复制节点及其整个子DOM 树。false:只会复制调用该方法的节点。
- normalize() 将当前节点和它的后代节点”规范化“。如果发现空文本节点,则将其删除;如果两个同胞节点是相邻的,则将其合并为一个文本节点。
Document 类型
Document 类型是 JavaScript 中表示文档节点的类型。在浏览器中,文档对象document 是 HTMLDocument 的实例,表示整个HTML 页面。document 是 window对象的属性。
特征:
- nodeType 等于 9
- nodeName 值为 "#document"
- nodeValue 值为null
- parentNode 值为null
- ownerDocument 值为null
- 子节点可以是DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment 类型
1. 文档字节点
- document.documentElement 取得对html 的引用
- document.body
- document.doctype 取得对<!doctype> 的引用
2. 文档信息
// 文档标题
document.title
// 完整的URL
document.URL
// 域名
document.domain
// 链接到当前页面的那个页面的URL
document.referrer
3. 定位元素
- getElementById() 接收一个参数,即要获取元素的ID
- getElementByTagName() 接收一个参数,即要获取元素的标签名
- getElementByName() 返回具有给定name 属性的所有元素
4. 特殊集合
- document.anchors
- document.applets
- document.forms
- document.images
- document.links
5. 文档写入
- write()
- writeln()
- open()
- close()
Element 类型
表示XML 或 HTML 元素。
特征:
- nodeType 等于1
- nodeName 值为元素的标签名
- nodeValue 值为null
- parentNode 值为Document 或 Element 对象
- 子节点可以是 Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference类型
可以通过 nodeName 或 tagName 属性来获取元素的标签名。
1. HTML 元素
标准属性:
- id
- title
- lang
- dir
- className
2. 属性操作
- getAttribute()
- setAttribute()
- removeAttribute()
3. attributes 属性
- getNamedItem(name)
- removeNamedItem(name)
- setNamedItem(node)
- item(pos)
4. 创建元素
document.createElement()
Text 类型
字面量解释的存文本或转义后的HTML 字符
特征:
- nodeType 等于3
- nodeName 值为 “#text”
- nodeValue 值为节点中包含的文本
- parentNode 值为Element 对象
- 不支持子节点
操作文本的方法:
- appendData(text) 向节点末尾添加文本text
- deleteData(offset, count) 从位置offset 开始删除 count 个字符
- insertData(offset, text) 在位置offset 插入 text
- replaceDate(offset, count, text) 用text 替换从位置offset 到 offset + count 的文本
- splitText(offset) 在位置offset 将当前文本节点拆分为两个文本节点
- substringData(offset, count) 提取从位置offset 到 offset + count 的文本
1. 创建文本节点
document.createTextNode()
2. 规范化文本节点
normalize()
let element = document.createElement("div");
element.className = "message";
let textNode = document.createTextNode("Hello!");
element.appendChild(textNode);
let anotherTextNode = document.createTextNode("Word");
element.appendChild(anotherTextNode);
alert(element.childNodes.length); // 2
element.normalize();
alert(element.childNodes.length); // 1
Comment 类型
注释
特征:
- nodeType 等于8
- nodeName 值为 “#comment”
- nodeValue 值为注释的内容
- parentNode 值为 Document 或 Element 对象
- 不支持子节点
Comment 类型与 Text 类型继承同一个基类(CharacterData),因此拥有除 splitText() 之外Text 节点所有的字符串操作方法。
创建注释节点:document.createComment()
CDATASection 类型
XML 中特有的CDATA 区块(只在XML 文档中有效),继承 Text 类型
特征:
- nodeType 等于4
- nodeName 值为"#cdata-section"
- nodeValue 值为CDATA 区块的内容
- parentNode 值为Document 或 Element 对象
- 不支持子节点
创建:document.createCDataSection()
DocumentType 类型
文档的文档类型信息
特征:
- nodeType 等于10
- nodeName 值为文档类型的名称
- nodeValue 值为null
- parentNode 值为 Document 对象
- 不支持子节点
该对象保存在 document.dotype 属性中。具有3个属性:name、entities、notations
DocumentFragment 类型
文档片,存储由节点(nodes)组成的文档结构,它的变化不会触发DOM树的重新渲染
特征:
- nodeType 等于11
- nodeName 值为"#document-fragment"
- nodeValue 值为null
- parentNode 值为null
- 子节点可以是 Element、ProcessingInstruction、Comment、Text、CDATASection 或 EntityReference
let fragment = document.createDocumentFragment();
let ul = document.getElementById("myList");
for(let i = 0;i < 3;++i) {
let li = document.createElement("li");
li.appendChild(document.createTextNode(`Item ${i + 1}`));
fragment.appendChild(li);
}
ul.appendChild(fragment);
Attr 类型
元素数据
特征:
- nodeType 等于2
- nodeName 值为属性名
- nodeValue 值为属性值
- parentNode 值为null
- 在 HTML 中不支持子节点
- 在 XML 中子节点可以是Text 或 EntityReference
该对象上有3个属性:name、value、specified
let attr = document.createAttribute("align");
attr.value = "left";
element.setAttributeNode(attr);
DOM 编程
动态脚本
引入外部文件和直接插入源代码
let script = document.createElement("script");
script.src = "foo.js";
document.body.appendChild(script);
// 另一种
script.appendChild(document.createTextNode("function sayHi(){alert('hi');}));
动态样式
let link = document.createElement("link");
link.ref = "stylesheet";
link.type = "text/css";
link.href = "style.css";
let head = ducument.getElementsByTagName("head")[0];
head.appendChild(link);
MutationObserver 接口
提供了监视对 DOM 树所做更改的能力
基本用法
1. observe(target[, options]) 方法
配置 MutationObserver
在 DOM 更改匹配给定选项时,通过其回调函数开始接收通知。接收两个参数:要观察其变化的DOM 节点,一个 MutationObserverInit 对象。
options:
属性 | 说明 |
---|---|
subtree | 布尔值,表示除了目标节点,是否观察目标节点的子树(后代) 如果是 false,则只观察目标节点的变化;如果是 true,则观察目标节点及其整个子树 |
attributes | 布尔值,表示是否观察目标节点的属性变化 默认为 false |
attributeFilter | 字符串数组,表示要观察哪些属性的变化 把这个值设置为 true 也会将 attributes 的值转换为 true 默认为观察所有属性 |
attributeOldValue | 布尔值,表示 MutationRecord 是否记录变化之前的属性值 把这个值设置为 true 也会将 attributes 的值转换为 true 默认为 false |
characterData | 布尔值,表示修改字符数据是否触发变化事件 默认为 false |
characterDataOldValue | 布尔值,表示 MutationRecord 是否记录变化之前的字符数据 把这个值设置为 true 也会将 characterData 的值转换为 true 默认为 false |
childList | 布尔值,表示修改目标节点的子节点是否触发变化事件 默认为 false |
例:
如下<body> 元素上任何属性发生变化都会被这个MutationObserver 实例发现,然后执行异步注册的回调函数。配置了元素后代的修改或其他属性修改都不会触发回调进入任务队列。
let observer = new MutationObserver(() => console.log('body 修改监听'));
observer.observe(document.body, { attributes: true});
document.body.className = 'foo';
// body 修改监听
2. 回调
回调函数接收两个参数:
- MutationRecord 实例的数组。包含的信息包括一个发生了什么的数组
- 观察变化的 MutationObserver 的实例
MutationRecord 的实例属性
属性 | 说明 |
---|---|
target | 被修改影响的目标节点 |
type | 字符串,表示变化的类型:attributes、characterData、childList |
oldValue | 如果在 MutationObserverInit 对象中启用( attributeOldValue 或 characterData OldValue |
attributeName | 对于"attributes"类型的变化,这里保存被修改属性的名字 其他变化事件会将这个属性设置为 null |
attributeNamespace | 对于使用了命名空间的"attributes"类型的变化,这里保存被修改属性的名字 其他变化事件会将这个属性设置为 null |
addedNodes | 对于"childList"类型的变化,返回包含变化中添加节点的 NodeList 默认为空 NodeList |
removedNodes | 对于"childList"类型的变化,返回包含变化中删除节点的 NodeList 默认为空 NodeList |
previousSibling | 对于"childList"类型的变化,返回变化节点的前一个同胞 Node 默认为 null |
nextSibling | 对于"childList"类型的变化,返回变化节点的后一个同胞 Node 默认为 null |
3. disconnect() 方法
告诉观察者停止观察变动。 可以通过调用其 observe() 方法来重用观察者。
observer.disconnect();
4. taskRecords() 方法
返回已检测到但尚未由观察者的回调函数处理的所有匹配 DOM 更改的列表,使变更队列保持为空。 此方法最常见的使用场景是在断开观察者之前立即获取所有未处理的更改记录,以便在停止观察者时可以处理任何未处理的更改。
5. 复用 MutationsObserver
let observer = new MutationObserver((mutationRecords) => console.log(mutationRecords.map(x => x.target)));
let childA = document.createElement("div");
let childB = document.createElement("span");
document.body.appendChild(childA);
document.body.appendChild(childB);
observer.observe(childA, {attributes: true});
observer.observe(childB, {attributes: true});
childA.setAttribute('foo', 'bar');
childB.setAttribute('foo', 'bar');
// [div, span]