1、节点层级
- 
documen节点是每个文档的根结点
 - 
文档元素是文档最外层元素,每个文档值呢个有一个文档元素,在HTML中,始终是
<html>元素 - 
元素节点表示HTML元素,属性节点表示属性,文档类型节点表示文档类型,注释节点表示住宿。
 
1、Node类型
- 
所以节点类型都继承Node类型,共享个相关的基本属性和方法。
 - 
每个节点都有nodeType属性,表示该节点类型
节点类型常量 值+描述 Node.ELEMENT_NODE 1元素节点 Node.ATTRIBUTE_NODE 2属性节点 Node.TEXT_NODE 3文本节点 Node.CDATA_SECTION_NODE 4CDATA节点 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符号节点 if(someNode.nodeType == Node.ELEMENT_NODE) { // ... } 
1、nodeName 与nodeValue
- 
保存有关节点的信息,属性值取决于节点类型。
if(someNode.nodeType == Node.ELEMENT_NODE) { // someNode.nodeName 表示标签名 // someNode.nodeValue 始终为null }- 对于元素 nodeName表示标签名, nodeValue始终为null
 
 
2、节点关系
- 
childrenNodes属性,包含NodeList实例NodeList是类数组,存储可以按位置存取的有序节点。NodeList不是Array实例,但是可以用中括号访问,也有length属性NodeList是一个对DOM结构的查询,DOM结构变化会自动反映出来
let firstChild = someNodes.childrenNodes[0] let secondChild = someNodes.childrenNodes.item(1) let count = someNodes.childrenNodes.length - 
parentNode属性,指向DOM树中的父元素 - 
childrenNodes中的每个节点都是同一列表中其他节点的同胞节点。previousSibing表示上一个兄弟nextSibing表示下一个兄弟
 - 
父节点的第一个子节点
firstChild,最后一个子节点lastChild - 
hasChildNodes()方法检测是否有子节点,等同于someNode.childNodes.length >= 1 - 
ownerDocument只想代表整个文档的文档节点的指针 
3、操纵节点
appendChild()- 在childNodes列表末尾添加节点,返回新添加的节点。
 - 如果是文档中已存在的节点,则会从之前的位置转移到新位置
- 如果传入第一个子节点,则会变成最后一个子节点
 
 
insertBefore()- 在childNodes列表特定位置添加节点,返回新添加的节点。
 - 两个参数:要插入的节点和参照节点
 - 如果参照节点为null,等同
appendChild() - 调用后会变成参照节点的前一个同胞节点
 
replaceChild()- 替换节点,删除已知节点,返回要替换的节点
 - 两个参数:要插入的节点和要替换的节点
 
removeChild()- 移除节点,而非替换,返回被移除的节点
 - 参数:要移除的节点
 
4、其他方法
cloneNode()返回与调用它的节点一模一样的节点。- 参数布尔值,表示是否深复制(复制节点及其整个子DOM树)
 - 不会复制添加到DOM节点的JavaScript属性,如事件处理程序。只复制HTML属性,以及可选的复制子节点。
 
normalize()处理文档子树中的文本节点,将空文本的文本节点或文本节点之间互为同胞关系删除或合并掉。
2、Document类型(9)
- 文档节点的类型,是HTMLDocument的实例,表示整个HTML页面
- nodeType: 9
 - nodeName: #document
 - nodeValue:null
 - parentNode:null
 - ownerDocument:null
 - 子节点可以是DocumentType(最多一个)、Element(最多一个)、ProcessingInstructoin或Comment类型
 
 
1、文档子节点
documentElement始终指向<html>元素body直接指向<body>元素doctype访问<!doctype>标签
2、文档信息
title包含<title>元素中的文本,显示在浏览器窗口或标题栏,可读可写URL当前页面的完整URLdomain页面的域名,- 可设置,但有限制,必须是原domain包含的值
 - 只能放松不能收紧
 - 修改成相同domain可以用来嵌套窗格通信
 
referrer链接到当前页面的那个页面的URL
3、定位元素
- 
getElementById()- 传入id,找到返回,没找到返回null
 
 - 
getElementByTagName()- 
传入标签名,返回NodeList,在HTML文档中返回
HTMLCollection对象 - 
HTMLCollectionnamedItem(),通过name属性取得某一项引用- 通过中括号获取
 
<img src="xxx" name="myImage"> // images.namedItem("myImage") // images["myImage"] - 
取得所有元素传入
* 
 - 
 - 
getElementsByName()- 返回具有给定name属性的所有元素
 
 
4、特殊集合
document.anchors包含文档所有带name属性的<a>元素document.forms包含文档所有<form>元素document.images包含文档所有<img>元素document.links包含文档所有带href属性的<a>元素
5、DOM兼容性检测
document.implementation
6、文档写入
- write()、writeln()、open()、close()
 
3、Element类型
- 表示XML或HTML元素,对外暴露出访问元素标签名、子节点和属性能力
- nodeType:1
 - nodeName:元素的标签名
 - nodeValue:null
 - parentNode:Document或Element对象
 
 - 可以通过nodeName或tagName获取标签名(HTML全大写,XML实际大小)
 
1、HTML元素
- 所有HTML元素都通过HTMLElement类型表示,包括其直接实例和间接实例
 - HTMLElement直接继承Element并增加了一些属性
- id:元素在文档中的唯一标识符
 - title:包含元素的额外信息,通常以提示条形式展示
 - lang:元素内容的语言代码
 - dir:语言书写方向
 - className:相当于class属性,指定元素css类
 
 
2、取得属性
getAttribute()- 传入的属性名与实际的属性名要一致
 - 不存在返回null
 - 能获取非HTML语言正式定义的自定义属性的值。
 - 属性名不区分大小写
 - 自定义属性应该用
data-前缀方便验证 - 所有属性都可以通过相应的DOM元素对象的属性来取得。
 - DOM元素对象的属性包括直接映射的五个属性和所有的公认(非自定义)属性。
 - 通过DOM对象访问的属性与
getAttribute()获得的属性区别- style属性
getAttribute()返回css字符串- DOM对象访问的属性是一个(CSSStyleDeclaration)对象
 
 - 事件(如onclick)
getAttribute()返回字符串形式的源代码- DOM对象访问的属性是一个JavaScript函数
 
 
 - style属性
 
3、设置属性
setAttribute()- 两个参数,要设置的属性名和属性值。
 - 如果属性名已存在,则会进行替换。
 - 适用于HTML属性,也适用自定义属性
 - 设置的属性名会规范为小写形式。
 - 也可以直接给DOM对象的属性赋值设置元素属性的值。
 - DOM对象上添加自定义属性,不会让他自动变成元素属性。
 
removeAttribute()- 删除属性的值,把整个属性完全从元素中去掉。
 
4、attributes属性
attributes属性包含一个NameedNodeMap实例,每个属性都表示为一个Attr节点。getNamedItem(name)返回nodeName属性等于name的节点removeNameedItem(name)删除nodeName属性等于name的节点setNamedItem(node)向列表中添加node节点,以其nodeName为索引- item(pos) 返回索引位置pos处的节点。
 
attributes属性中的每个节点的nodeName是对应属性的名字,nodeValue是属性的值。- 最有用的场景是需要迭代元素上所有属性的时候。
 
5、创建元素
document.createElement()创建新元素- 接收一个参数,即要创建元素的标签名。
 - 创建之后可以为其添加属性
 - 创建之后需要使用添加方法添加到文档中。
 
6、元素后代
- 
childNodes属性包含元素所有子节点,包括其他元素、文本节点、注释或处理指令。
 - 
执行某个操作前可以先检测一下节点的nodeType(或者使用后面的children)
for(let i = 0, len = element.childNodes.length; i < len; i++) { if(element.childNodes[i].nodeType === 1) { ... } } 
4、Text类型
- Text节点由Text类型表示,包含按字面解释的纯文本,也可以包含转义后的HTML字符串。
- nodeType:3
 - nodeName:#text
 - nodeValue:节点保存的文本
 - parentNode:Element对象
 - 不支持子节点
 
 - Text节点中包含的文本可以通过nodeValue属性访问,也可以通过data属性访问。
appendData(text)向节点末尾添加文本textdeleteDate(offset, count)从位置offset开始删除count个字符insertData(offset, text)在offset位置插入textreplaceData(offset, count, text)用text替换offset到offset+count的文本splitText(offset)在位置offset将文本节点拆成两个文本节点substringData(offset, count)提取从位置offset到offset+count的文本length属性获取本本节点中包含的字符数量。等于nodeValue.length和data.length
 - 默认情况下每个元素最多只能有一个文本节点。
 - 修改文本节点的内容时,大于号,小于号或引号会被转义
 
1、创建文本节点
document.createTextNode()创建新文本节点- 接收一个参数,即要插入节点的文本。
 - 创建之后需要使用添加方法添加到文档中。
 
2、规范化文本节点
normalize()
3、拆分文本节点
splitText()
5、Comment类型
- 注释通过Comment类表示
- nodeType:8
 - nodeName:#comment
 - nodeValue:注释的内容
 - parentNode:Document或Element对象
 - 不支持子节点
 
 - Comment与Text类型继承同一个基类(CharacterData),拥有除了
splitText()外的所有方法。 - 注释的内容可通过nodeValue或data获取。
 document.createComment()创建注释节点
6、CDATASection类型
- XML特有,继承Text类型。
- nodeType:4
 - nodeName:#cdata-section
 - nodeValue:CDATA区块内容
 - parentNode:Document或Element对象
 - 不支持子节点
 
 document.createCDataSection()传入节点内容创建
7、DocumentType类型
- 包含文档的文档类型(doctype)信息
- nodeType:10
 - nodeName:文档类型的名称
 - nodeValue:null
 - parentNode:Document
 - 不支持子节点
 
 - 不支持动态创建,只能在解析文档代码时创建。
- 保存在
document.doctype中 - 包含三个属性:name、entities和notations
- name表示文档类型的名称
 - entities 是这个文档类型描述的实体的NamedNodeMap 通常为空
 - natations 是这个文档类型描述的表示法的NamedNodeMap 通常为空
 
 
 - 保存在
 
8、DocumentFragment类型
- “轻量级”文档,能够包含和操作节点,却没有完整文档那样额外的消耗。
- nodeType:11
 - nodeName:#document-fragment
 - nodeValue:null
 - parentNode:null
 
 - 不能直接将文档片段添加到文档,作用是充当其他要被添加到文档的节点的仓库。
 document.createDocumentFragment()创建文档片段- 文档片段从Node类型继承了所有文档类型具备的可执行DOM操作的方法。如果文档中的一个节点被添加到文档片段,则该节点会文档树中移除,不再渲染。
 - 通过文档片段可以避免多次渲染,减少性能消耗。
 
9、Attr类型
- 
元素数据在DOM中通过Attr类型表示。是存在于元素attributes属性中的节点
- nodeType:2
 - nodeName:属性名
 - nodeValue:属性值
 - parentNode:null
 - HTML中不支持子节点,XML中子节点可以是Text或EntityReference
 
 - 
三个属性
- name 属性名
 - value 属性值
 - specified 表示属性使用的是默认值还是被指定的值
 
 - 
document.createAttrubute()创建新的Attr节点let attr = document.createAttribute('align') attr.value = 'left' element.setAttributeNode(attr) - 
getAttributeNode()获取Attr节点 - 
以上方法都不建议使用。
 
2、DOM编程
1、动态脚本
- 
动态脚本是在页面初始加载的时候不存在,之后又通过DOM包含的脚本。
 - 
两种方式通过
<script>动态为网页添加脚本:引入外部文件和直接插入源代码- 引入外部文件
 
function loadScript(url) { let script = document.createElement('script') script.src = url document.body.appendChild(script) }- 直接插入源代码
 
function loadScriptString(code) { let script = docuemnt.createElement('script') script.type = "text/javascript" try { // 兼容Safari3以下 script.appendChild(document.createTextNode(code)) }catch(ex) { // 兼容旧版IE script.text = code } document.body.appendChild(script) } - 
使用innerHtml创建的
<script>元素永远不会执行 
2、动态样式
- 
<link>用于包含CSS外部文件function loadStyles(url) { let link = document.createElemt('link') link.rel = 'stylesheet' link.type = 'text/css' link.href = 'url' let head = document.getElementsByTagName('head')[0] head.appendChild(link) } - 
<style>用于添加嵌入样式function loadStylesString(css) { let style = documnet.createElement('style') style.tyle = 'text/css' try{ style.appendChild(document.createTextNode(css)) }catch(ex) { // 兼容IE 但容易崩溃 style.stylesheet.cssText = css } let head = document.getElementsByTagName('head')[0] head.appendChild(style) } 
3、操作表格
- 
<table>的属性和方法caption, 指向<caption>元素的指针(如果存在)tBodies,包含<tbody>元素的HTMLCollectiontFoot,指向<tfoot>元素(如果存在)tHead,指向<thead>元素(如果存在)rows,包含表示所有行的HTMLCollectioncreateTHead(),创建<thead>元素,放到表格中,返回引用createTFoot(),创建<tfoot>元素,放到表格中,返回引用createCaption(),创建<caption>元素,放到表格中,返回引用deleteTHead(),删除<thead>元素deleteTFoot(),删除<tfoot>元素deleteCaption(),删除<caption>元素deleteRow(pos),删除给定位置的行insertRow(pos),在行集合中给定位置插入一行
 - 
<tbody>的属性和方法rows,包含<tbody>元素中所有行的HTMLCollectiondeleteRow(pos),删除给定位置的行insertRow(pos),在行集合中给定位置插入一行,返回该行引用
 - 
<tr>的属性和方法cells,包含<tr>元素所有表元的HTMLCollectiondeleteCell(pos),删除给定位置的表元insertCell(pos),在表元集合给定位置插入一个表元,返回该表元的引用
 
4、使用NodeList
- 理解NodeList对象和NamedNodeMap、HTMLCollection
 - 这3个集合类型都是"实时的",文档结构的变化会实时的在它们身上反应出来。
 - 最好限制操作NodeList的次数,因为每次查询都会搜索整个文档,最好把查询到的NodeList缓存起来。
 
3、MutationObserver接口
- 可以在DOM修改时异步执行回调
 - 可以用来观察整个文档、DOM树的一部分,或整个元素。
 - 可以观察元素属性、子节点、文本,或者前三者任意组合的变化
 
1、基本用法
- 通过调用MutationObserver构造函数传入回调函数来创建
 
1、observe()方法
- 
与DOM进行关联,接收两个必需参数:
- 要观察其变化的DOM节点
 - 一个MutationObserverInit 对象
 
 - 
MutationObserverInit 对象是控制观察哪方面的变化,是一个键值对形式配置选项的字典。
let observer = new MutationObserver(()=>console.log('<body> attributes changed')) observer.observe(document.body, {attributes: true})<body>元素任何属性的变化都会被MutationObserver实例发现- 会异步执行注册的回调函数
 
 
2、回调与MutationRecord
- 
每个回调都会收到一个MutationRecord实例的数组。包含的信息包括发生了什么变化,以及DOM的哪一部分受到了影响。
 - 
可能有多个满足观察条件的事件,每次执行回调都会传入一个包含按顺序入队的MutationRecord实例的数组
let observer = new MutationObserver( (mutationRecords) => console.log(mutationRecords)); observer.observe(document.body, { attributes: true }); document.body.setAttribute('foo', 'bar'); // [ // { // addedNodes: NodeList [], // attributeName: "foo", // attributeNamespace: null, // nextSibling: null, // oldValue: null, // previousSibling: null // removedNodes: NodeList [], // target: body // type: "attributes" // } // ] document.body.className = 'foo' document.body.className = 'bar' document.body.className = 'baz' // [MutationRecord, MutationRecord, MutationRecord] - 
MutationRecord 实例属性
属性 说明 target 被修改影响的目标节点 type 变化类型,"attributes"、"characterData"或"childList" oldValue 旧值,需要在MutationObserverInit 中启用 attributeName 对"attributes" 类型变化,保存被修改属性名字 attributeNamespace 对使用命名空间的"attributes" 类型变化,保存被修改属性名字 addedNodes 对应childList变化,包含变化中添加节点的NodeList removedNodes 对应childList变化,包含变化中删除节点的NodeList previousSibling 对应childList变化,返回变化节点的前一个同胞Node nextSibling 对应childList变化,返回变化节点的后一个同胞Node  - 
传给回调函数的第二个实例是观察变化的MutationObserver实例。
 
3、disconnect() 方法
- 提前终止执行回调,可以调用
disconnect()方法 - 同步调用
disconnect()后,不仅会停止此后事件的回调,也会抛弃已经加入任务队列要异步执行的回调 
4、复用MutationObserver
- 多次调用
observer()方法,可以复用一个MutationObserver对象观察多个不同的目标节点。 disconnect()方法是个"一刀切"的方案,会停止观察所有目标
5、重用MutationObserver
- 调用
disconnect()并不会结束MutationObserver 的生命。还可以重新使用这个观察者,再将它关联到新的目标节点。 
2、MutatoinObserverInit 与 观察范围
- MutatoinObserverInit 用来控制对目标节点的观察范围。
 
| 属性 | 说明 | 
|---|---|
| subtree | 是否观察目标节点子树,默认false | 
| attributes | 是否观察目标节点属性变化,默认false | 
| attributeFilter | 字符串数组,表示观察哪些属性,默认所有 | 
| attributeOldValue | 是否记录变化之前的属性值,默认false | 
| characterData | 是个观察字符数据修改,默认false | 
| characterDataOldValue | 是否记录变化之前的字符数据,默认false | 
| childList | 目标节点子节点是否触发变化事件,默认false | 
1、观察属性
- MutationObserver可以观察节点属性的添加、移除和修改。
 - 需要在MutatoinObserverInit 设置attributes为true
 - 默认观察所有属性,可以通过attributeFilter 设置白名单
 - 保存原有属性,设置attributeOldValue
 
2、观察字符数据
- MutationObserver可以观察文本节点(Text、Comment)中字符的添加、删除和修改。
 - 需要在MutatoinObserverInit 设置characterData为true
 - 保存原有字符数据,设置characterDataOldValue
 
3、观察子节点
- MutationObserver可以目标节点子节点的添加和移除
 - 需要在MutatoinObserverInit 设置childList为true
 - 对子节点重新排序会报高两次事件变化
 
4、观察子树
- 默认情况MutationObserver观察范围是一个元素及其子节点的变化。可以把范围扩展到这个元素的子树。
 - 需要在MutatoinObserverInit 设置subtree为true
 
3、异步回调与记录队列
- 出于性能考虑而设计的,核心是异步回调与记录队列模型。每次变化会保存在MutationRecord实例中,然后添加到记录队列。
 - 这个队列对每个MutationObserver实例都是唯一的,是所有DOM事件变化的有序列表。
 
1、记录队列
- 仅当之前没有已排期的微任务回调时,才会将观察者注册的回调作为微任务调度到任务队列上,可以保证记录队列的内容不会被回调处理两次。
 - 回调执行后,记录队列会清空,内容会丢弃。
 
2、takeRecords() 方法
- 调用MutationObserverd的
takeRecords()方法可以清空记录队列,取出并返回其中所有MutationRecord实例。 - 在希望断开与观察目标来的联系,又希望处理由于调用
disconnect()而被抛弃的记录队列中的MutationRecord实例时使用。 
4、性能、内存与垃圾回收
- 相比之前的
MutaionEvent有了很大进步,但仍然有代价。 
1、MutationObserver的引用
- MutationObserver实例与目标之前的引用关系时非对称的,是弱引用。不会妨碍垃圾回收程序回收程序回收目标节点。
 - 目标节点对MutationObserver是强引用,如果目标节点从DOM中移除,二后被垃圾回收,则关联的MutationObserver 也会被垃圾回收。
 
2、MutationRecord的引用
- MutationRecord实例至少包含对已有DOM节点的一个引用,如果变化的是childList类型,则会包含多个节点的引用。
 - 如果要保存变化记录,保存MutationRecord实例,也就会保存其引用节点。因此会妨碍这些节点被回收。
 - 可以从每个MutationRecord中抽取最有用信息,保存到一个新对象里,然后抛弃MutationRecord,这样可以尽快地释放内存。