DOM

文档对象模型(DOM)是HTML和XML文档的编程借口。DOM表示由多层节点构成的文档,通过它开发者可以添加、删除和修改页面的各个部分。

文章目录

  • DOM
  • 一、节点层级
    • 1.Node类型
    • 2.Document类型
    • 3.Element类型
    • 4.Text类型
    • 5.Comment属性
    • 6.CDATASection属性
    • 7.DocumentType属性
    • 8.DocumentFragment属性
    • 9.Attr属性
  • 二、DOM编程
    • 1.动态脚本
    • 2.动态样式
    • 3.操作表格
    • 4.使用NodeList
  • 三、MutationObserver接口
    • 1.基本用法
    • 2.MutationObserverInit与观察范围
    • 3.异步回调与记录队列
    • 4.性能、内存和垃圾回收
  • 总结

一、节点层级

任何HTML和XML文档都可以用DOM表示为一个由节点构成的层级结构。节点分很多类型,每种类型对应文档中不同的信息或标记,也都有自己不同的特性、数据和方法,而且与其他类型有某种关系。这些关系构成了层级,让标记可以表示一个以特定节点为根的树形结构。

document节点表示每个文档的根节点。在这里,根节点的唯一子节点是<html>元素,称之为文档元素。文档元素是文档最外层的元素,所有其他元素都存在与这个元素之内。每个文档只能有一个文档元素 。

1.Node类型

Node借口在JavaScript中被实现为Node类型,在除IE之外的所有浏览器中都可以直接访问这个类型。在JavaScript中,所有节点类型都继承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)

nodeName和nodeValue
nodeName和nodeValue保存着有关节点的信息。这两个属性的值完全取决于节点类型。
节点关系
文档中的所有节点都与其他节点有关系。每个节点都有一个childNode属性,其中包含一个NodeList的实例。NodeList是一个类数组对象,用于存储可以按位置存取的有序节点。
每一个节点都有一个parentNode属性,指向DOM树中的父元素。childNode中的所有节点都有同于个父元素,因此它们的parentNode属性都指向同一个节点。此外,childNodes列表中的每个节点都是同一个列表其他节点的同胞节点。而使用previousSiblingnextSibling可以在这个列表的节点间导航。
父节点和它的第一个及最后一个子节点也有专门的属性:firstChildlastChild分别指向childNodes中的第一个和最后一个。

hasChildNodes()方法如果返回true则说明节点有一个或多个子节点。ownerDocument属性是一个指向代表整个文档的文档节点的指针。

操作节点
appendChild()用于在childNodes列表末尾添加节点,返回新添加的节点。
代码如下(示例):

//添加新节点
let returnedNode=someNode.appendChild(newNode);
alert(returnedNode==newNode);//true
alert(someNode.lastChild==newNode);//true
//已有节点转移
let returnedNode=someNode.appendChild(someNode.firstChild);
alert(returnedNode==someNode.firstChild);//false
alert(returnedNode==someNode.lastChild);//true

insertBefore()方法把节点放在childNodes中的特定位置,接收两个参数:要插入的节点和参照节点,插入的节点会变成参照节点的前一个同胞节点如果参照节点是null,则放到最后。
appendChild()方法和insertBefore()方法在插入节点时不会删除任何已有的节点 。
代码如下(示例):

//作为最后一个子节点插入
returnedNode=someNode.insertBefore(newNode,null);
alert(newNode==someNode.lastChild);//true
//作为新的第一个子节点插入
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()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之。使用replaceChild()插入一个节点后,所有关系指针丢会被从替换的节点复制过来了。
代码如下(示例):

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

removeChild()方法移除节点,接收一个参数,即要移除的节点。
代码如下(示例):

//删除第一个子节点
let formerFirstNode=someNode.removeChild(someNode.firstChild);
//删除最后一个子节点
let formerLastNode=someNode.removeChild(someNode.lastChild);

其他方法
cloneNode()会返回与调用它的节点一模一样的节点,接收一个布尔值参数,表示是否深复制。在传入true参数时,会进行深复制,即复制节点及其整个子DOM树。如果传入false则只会复制调用该方法的节点。复制返回的节点属于文档所有,但尚未指定父节点,所以称为孤儿节点
normalize()方法处理文档子树中的文本节点。

2.Document类型

Document类型是JavaScript中表示文档节点的类型。在浏览器中,文档对象document是HTMLDocument的实例(HTMLDocument继承Document),表示整个HTML页面。document是window对象的属性,因此是一个全局对象。document对象可用于获取关于页面的信息以及操纵其外观和底层结构。Doccument类型的节点有以下特征:

  • nodeType等于9
  • nodeName值为“#document”
  • nodeValue值为null
  • parentNode值为null
  • ownerDocument值为null
  • 子节点可以是DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment类型。

1.文档子节点
DOM提供两个访问子节点的快捷方式。第一个是documentElement属性,始终指向HTML页面中的<html>元素。Document类型另一种可能的子节点是DocumentType。<!doctype>标签是文档中独立的部分,其信息可以通过doctype属性来访问。
document还有一个body属性直接指向<body>元素。

let body=document.body;//取得对<body>的引用

2.文档信息
document还提供浏览器所加载页面的信息。第一个属性是title,包含<title>元素的文本,通常显示在浏览器窗口或标签页的标题栏。通过这个属性可以读写页面的标题修改后的标题也会反映在浏览器标题栏上,但修改title属性并不会改变<title>元素。
代码如下(示例):

//读取文档标题
let originalTitle=document.title;
//修改文档标题
document.title="New page title";

document还有3个属性:URL、domain和referrer。其中,URL包含当前页面的完整URL(地址中的URL),domain包含页面的域名,而referrer包含链接到当前页面的那个页面的URL。
代码如下(示例):

//取得完整的URL
let url=document.URL;
//取得域名
let domain=document.domain;
//取得来源
let referrer=document,referrer;

3.定位元素
getElementById()getElementsByTagName()方法可以获取某个或某组元素的引用。
getElementById()方法接受一个参数,即要获取元素的ID,如果找到了则返回这个元素,如果没找到则返回null。参数ID必须跟元素在页面中id属性完全匹配,包括大小写 。
代码如下(示例):

<div id="myDiv">Some text</div>let div=document.getElementById("myDiv");//取得对这个<div>元素的引用
let div=document.getElementById("mydiv");//null

getElementsByTagName()方法接收一个参数,即要获得元素的标签名,返回包含零个或多个元素的NodeList。在HTML文档中,这个方法返回一个HTMLCollection对象。要获取文档的所有元素,可以给getElementsByTagName()传入星号(*)。
代码如下(示例):

let images=document.getElementsByTagName("img");
alert(images.length);//图片数量
alert(images[0].src);//第一张图片的src属性
alert(images.tiem[0].src);//同上
//额外方法通过标签的属性获得
<img src="myimage.gif" name="myImage">
let myImage=images.namedItem("myImage");

getElementsByName()方法返回具有给定name属性的所有元素,最长用于单选按钮。

4.特殊集合

  • document.anchors:包含文档中所有带name属性的<a>元素。
  • document.applets:包含文档中所有<applet>元素(废弃)。
  • document.forms:包含文档中所有<form>元素。
  • document.images:包含文档中所有<img>元素。
  • document.links:包含文档中所有带href属性的<a>元素。

5.DOM兼容性检测
hasFeature()方法接受两个参数:特殊名称和DOM版本。如果浏览器支持指定的特性和版本,则hasFeature()方法返回true。

6.文档写入
document对象能向网页输出流中写入内容,4个方法:write()writeln()open()close()。其中,write()和writeln()方法都接收一个字符串参数,可以在页面加载期间 将这个字符串写入网页中,如果在页面加载之后再调用document.write()则输出的内容重写整个页面。write()简单地写入文本,而writeln()还会在字符串末尾追加一个换行符。
代码如下(示例):

//write会写成一行
document.write("Hello World!");
document.write("Have a nice day!");


代码如下(示例):

//writeln会分行
document.writeln("Hello World!");
document.writeln("Have a nice day!");


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

3.Element类型

Element表示XML或HTML元素,对外暴露出访问元素标签名、子节点和属性的能力。
Element类型的节点具有以下特征:

  • nodeType等于1
  • nodeName值为元素的标签名
  • nodeValue值为null
  • parentNode值为Document或Element对象
  • 子节点可以是Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference类型

1.HTML元素
所有HTML元素都通过HTMLElement类型表示,包括其直接实例和间接实例。HTMLElement直接继承了Element并增加了一些属性。每个属性都有对应下列属性之一,它们是所有HTML元素上都有的标准属性:

  • id,元素在文档中的唯一标识符
  • title,包含元素的额外信息,通常以提示条形式展示
  • lang,元素内容的语言代码
  • dir,语言的书写方向
  • className,相当于class属性,用于指定元素的CSS类

代码如下(示例):

<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
let div=document.getElementById("myDiv");
//读取元素属性
alert(div.id);//myDiv
alert(div.className);//bd
alert(div.name);//Body text
//修改元素属性
div.lang="fr";
div.dir="rtl"

2.取得属性
每个元素都有零个或多个属性,通常用于为元素或其他内容附加更多信息,包括3个方法:getAttribute()setAttribute()removeAttribute()
代码如下(示例):

//getAttribute()主要用于取得自定义的属性
<div id="myDiv" class="bd"  my_special_attribute="hello"></div>
let div=document.getElementById("myDiv");
alert(div.getAttribute(id));//myDiv
alert(div.getAttribute(class));//bd
alert(div.getAttribute(my_special_attribute));//hello

3.设置属性
setAttribute()方法接收两个参数:要设置的属性名和属性的值。如果属性已经存在则替换原来的值,否则则以指定的值创建属性。
代码如下(示例):

div.setAttribute("id","someOtherId");

直接给DOM对象的属性赋值也可以设置元素属性的值。在DOM对象上添加自定义属性,不会自动让它变为元素的属性:

div.id="someOtherId";
//自定义属性
div.mycolor="red";
alert(div.getAttribute("mycolor"));//null

4.attributes属性
Element类型是唯一使用Attributes属性的DOM节点类型。attributes属性包含一个NamedNodeMap实例,是一个类似NodeList的“实时”集合。attributes属性最有用的场景是需要迭代元素上所有属性的时候。
NamedNodeMap对象包含下列方法:

  • getNamedItem(name),返回nodeName属性等于name的节点
  • removeNamedItem(name),删除nodeName属性等于name的节点
  • setNamedItem(name),向列表中添加 node节点,以其nodeName为索引
  • item(pos),返回索引位置pos处的节点

5.创建元素
document.createElement()方法创建新元素,但需要把元素添加到树上。

4.Text类型

Text节点由Text类型表示,包含按字面解释的纯文本,也可以包含转义后的HTML字符,但不包含HTML代码。Text类型的节点具有以下特征:

  • nodeType等于3
  • nodeName值为“#text”
  • nodeValue值为节点中包含的文本
  • parentNode值为Element对象
  • 不支持子节点

操作文本的方法:

  • appendData(text),向节点末尾添加文本text
  • deleteData(offset,count),从位置offset开始删除count个字符
  • insertData(offset,text),在位置offset插入text
  • replaceData(offset,count,text),用text替换从位置offset到offset+count的文本
  • splitText(offset),在位置offset将当前文本节点拆分为两个文本节点
  • substringData(offset,count),提取从位置offset到offset+count的文本

1.创建文本节点
document.createTextNode()可以用来创建新文本节点,它接收一个参数,即要插入节点的文本。
代码如下(示例):

let element=document.createElement("div");
element.className="message";
let textNode=document.createTextNode("Hello World");
element.appendChild(textNode);
document.body.appendChild(element);

2.规范化文本节点
normalize()方法在包含两个或多个相邻文本节点的父节点上被调用时,所有同胞文本节点会被合并为一个文本节点。
代码如下(示例):

let element=document.createElement("div");
element.className="message";
let textNode=document.createTextNode("Hello World");
element.appendChild(textNode);
let anotherTextNode=document.createTextNode("yippee!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
alert(element.childNodes.length);//2
element.normalize();
alert(element.childNodes.length);//1
alert(element.firstChild.nodeValue);//"Hello World!yippee!"

3.拆分文本节点
splitText()方法可以在指定的偏移位置拆分nodeValue,讲一个文本节点拆分为两个文本节点。拆分后,原来的文本节点包含开头到偏移位置前的文本,新文本节点包含剩下的文本。这个方法返回新的节点。
代码如下(示例):

let element=document.createElement("div");
element.className="message";
let textNode=document.createTextNode("hello world!");
element.appendChild(textNode);
document.body.appendChild(element);
let newNode=element.firstChild.splitText(5);
alert(element.firstChild.nodeValue);//"hello"
alert(newNode.nodeValue);//" world"
alert(element.childNodes.length);//2

5.Comment属性

DOM注释通过Comment类型表示。Comment类型的节点具有以下特征:

  • nodeType等于8
  • nodeName值为“#comment”
  • nodeValue值为注释内容
  • parentNode值为Document或Element对象
  • 不支持子节点

Comment类型与Text类型继承同一个基类,因此拥有除splitText()之外Text节点的所有的字符串操作方法。注释节点可以作为父节点的子节点来访问。
代码如下(示例):

<div id="myDiv"><!-- A comment --></div>
let div=document.getElementById("myDiv");
let comment=div.firstChild;
alert(comment.data)
//创建注释节点
let comment=document.createComment("A comment");

6.CDATASection属性

CDATASection类型表示XML中特有的CDATA区块。CDATASection类型继承Text类型,因此具有包括splitText()之内的所有字符串操作方法。CDATASection类型的节点具有以下特征:

  • nodeType等于4
  • nodeName值为“#cdata-section”
  • nodeValue值为CDATA区块的内容
  • parentNode值为Document或Element对象
  • 不支持子节点

7.DocumentType属性

DocumentType类型的节点包含文档的文档类型(doctype)信息,具有以下特征:

  • nodeType等于10
  • nodeName值为文档类型的名称
  • nodeValue值为null
  • parentNode值为Document对象
  • 不支持子节点

8.DocumentFragment属性

DocumentFragment类型是唯一一个在标记中没有对应表示的类型。DOM将文档片段定义为“轻量级”文档,能够包含和操作节点,却没有完整文档那样额外的消耗。DocumentFragment节点具有以下特征:

  • nodeType等于11
  • nodeName值“#document-fragment”
  • nodeValue值为null
  • parentNode值为null
  • 子节点可以是Element、ProcrssingInstruction、Comment、Text、CDATASection或EntityReference

不能直接把文档片段添加到文档。文档片段的作用是充当其他要被添加到文档的节点的仓库。
代码如下(示例):

<ul id="myList"></ul>let fragment=document.createDocumentFragment();
let ul=document.getElementById("myList");
for(let i=0;i<3;++i){let li=document.createElement("li");//创建li元素li.appendChild(document.createTextNode(`Item ${i+1}`));//创建添加文本节点fragment.appendChild(li);//添加到文本片段
}
ul.appendChild(fragment);//将文本片段添加到ul元素

9.Attr属性

元素数据在DOM中通过Attr类型表示。Attr类型构造函数和原型在所有浏览器中都可以直接访问。
Attr节点具有以下特征:

  • nodeType等于2
  • nodeName值属性名
  • nodeValue值为属性值
  • parentNode值为null
  • 在HTML中不支持子节点
  • 在XML中子节点可以是Text或EntityReference

二、DOM编程

1.动态脚本

有两种方式通过<script>动态为网页添加脚本:引入外部文件和直接插入源代码。
代码如下(示例):

//动态加载外部文件
<script src="foo.js"></script>
//DOM编程创建
let script=document.createElement("script");
script.src="foo.js";
document.body.appendChild(script);
//抽象函数
function loadScript(url){let script=document.createElement("script");script.src=url;document.body.appendChild(script);
}//嵌入源代码
<script>function sayHi(){alert("hi");}
</script>
//DOM在Fierfox、Safair、Chrome和Opera中实现
let script=document.createElement("script");
script.appendChild(document.createTextNode("function sayHi(){alert('hi');}"));
document.body.appendChild(script);
//抽象为跨浏览器的函数
function loadScriptString(code){var script=document.createElement("script");script.type="text/javascript";try{script.appendChild(document.createTextNode(code));}catch(ex){script.text=code;}document.body.appendChild(script);
}

2.动态样式

CSS样式在HTML页面中可以通过<link>和<style>两个元素加载。
代码如下(示例):

//外部link加载
<link rel="stylesheet" type="text/css" href="styles.css">
//DOM编程实现link
let link=document.createElement("link");
link.rel="stylesheet";
link.type="text/css";
link.href="styles.css";
let head=document.getElementsByTagName("head")[0];
head.appendChild(link);
//抽象为通用函数
function loadStyles(url){link=document.createElement("link");link.rel="stylesheet";link.type="text/css";link.href=url;let head=document.getElementsByTagName("head")[0];head.appendChild(link);
}
//嵌入式style加载
<style type="text/css">
body{background-color:red;
}
</style>
//嵌入式在Fierfox、Safair、Chrome和Opera中实现DOM编程
let style=document.createElement("style");
style.type="text/css";
style.appendChild(document.createTextNode("body{background-color:red;}"));
let head=document.getElementsByTagName("head")[0];
head.appendChild(style);
//通用函数
function loadStyleString(css){let style=document.createElement("style");style.type="text/css";try{style.appendChild(document.createTextNode(css));}catch(ex){style.styleSheet.cssText=css;}let head=document.getElementsByTagName("head")[0];head.appendChild(style);}
}

3.操作表格

为了方便创建表格,HTML DOM给<table>、<tbody>、<tr>元素添加了一些属性和方法。
<table>元素添加了以下属性和方法:

  • caption,指向<caption>元素的指针(如果存在)
  • tBodies,包含<tbody>元素的HTMLCollection
  • tFoot,指向<tfoot>元素(如果存在)
  • tHead,指向<thead>元素(如果存在)
  • rows,包含表示所有行的HTMLCollection
  • createTHead(),创建<thead>元素,放到表格中,返回引用
  • createTFoot(),创建<tfoot>元素,放到表格中,返回引用
  • createCaption(),创建<caption>元素,放到表格中,返回引用
  • deleteTHead(),删除<thead>元素
  • deleteTFoot(),删除<tfoot>元素
  • deleteCaption(),删除<caption>元素
  • deleteRow(pos),删除给定位置的行
  • insertRow(pos),在行集合给定位置插入一行

<tbody>元素添加了以下属性和方法:

  • rows,包含元素中所有行的HTMLCollection
  • deleteRow(pos),删除给定位置的行
  • insertRow(pos),在行集合给定位置插入一行,返回该行的引用

<ttr>元素添加了以下属性和方法:

  • cells,包含<tr>元素所有表元的HTMLCollection
  • deleteCell(pos),删除给定位置的表元
  • insertCell(pos),在表元集合给定位置插入一个表元,返回该表元的引用

代码如下(示例):

//创建表格
let table=document.createElement("table");
table.border=1;
table.width="100%";
//创建表体
let tbody=document.createElement("tbody");
table.appendChild(tbody);
//创建第一行
tbody.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1"));
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1"));
//创建第二行
tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));
//把表格添加到文档主体上
document.body.appendChild(table);

4.使用NodeList

NodeList、NamedNodeMap、HTMLCollection这3个集合类型都是“实时的”,一位置文档结构的便会会实时地在它们身上反映出来,因此它们的值始终代表最新的状态。实际上,NodeList就是基于DOM文档的实时查询。

三、MutationObserver接口

MutationObserver接口可以在DOM被修改时异步回调。使用MutationObserver可以观察整个文档、DOM树的一部分,或某个元素。此外还可以观察元素属性、子节点、文本,或者前三者任意组合的变化。

1.基本用法

MutationObserver的实例要通过调用MutationObserver构造函数并传入一个回调函数来创建。

let observer=new MutationObserver ( () => console.log('DOM was mutated!'));

1.observe()方法
新创建的MutationObserver实例不会关联DOM的任何部分,需要使用observe()方法。这个方法接收两个必需的参数:要观察其变化的DOM节点,以及一个MutationObserverInit对象。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
//回调并非与实际的DOM变化同步执行

2.回调与MutationRecord
每个回调都会收到一个MutationRecord实例的数组。MutationRecord实例包含的信息包括发发生了什么变化,以及DOM的哪一部分受到了影响。
代码如下(示例):

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"
//    }
// ]

3.disconnect()方法
默认情况下,只要被观察的元素不被垃圾回收,MutationObserver的回调就会响应DOM变化时间,从而被执行。要提前终止执行回调,可以调用disconnect()方法。
4.复用MutationObserver()方法
多次调用observe()方法,可以复用一个MutationObserver对象观察多个不同的目标节点。
5.重用MutationObserver()方法
调用disconnect()并不会结束MutationObserver的生命,还可以重新使用这个观察者,再将它关联到新的目标节点。

2.MutationObserverInit与观察范围

1.观察属性
MutationObserver可以观察节点属性的添加、移除和修改。要为属性变化注册回调,需要在MutationObserverInit对象将attributes属性设置为true。
代码如下(示例):

let observer=new MutationObserver((mutationRecords) => console.log(mutationRecords));
observer.observe(document.body,{attributes:true});
//添加属性
document.body.setAttribute('foo','bar');
//修改属性
document.body.setAttribute('foo','baz');
//移除属性
document.body.removeAttribute('foo');
//全都记录下来了

如果想观察某个或某几个属性,可以使用attributeFilter属性来设置白名单,即一个属性名字符串数组。
代码如下(示例):

let observer=new MutationObserver((mutationRecords) => console.log(mutationRecords));
observer.observe(document.body,{attributeFilter:['foo']});
//添加白名单属性
document.body.setAttribute('foo','bar');
//添加被排除的属性
document.body.setAttribute('quz','baz');
//只有foo属性的变化被记录

如果想在变化记录中保存属性原来的值,可以将attributeOldValue属性设置为true。
代码如下(示例):

let observer=new MutationObserver((mutationRecords) => console.log(mutationRecords.map((x) => x.oldValue)));
observer.observe(document.body,{attributeOldValue:true});
document.body.setAttribute('foo','bar');
document.body.setAttribute('foo','baz');
document.body.setAttribute('foo','qux');
//每次变化都保留了上一次的值
//[null,'bar','baz']

2.观察字符数据
MutationObserver可以观察文本节点中字符的添加、删除和修改。要为字符数据注册回调,需要在MutationObserverInit对象中将characterData属性设置为true。
3.观察子节点
MutationObserver可以观察目标节点子节点的添加和移除。要观察子节点,需要在MutationObserverInit对象中将childList属性设置为true。
4.观察子树
默认情况下,MutationObserver将观察的范围限定为一个元素及其子节点的变化。可以把观察的范围扩展到这个元素的子树,需要在MutationObserverInit对象中将subtree属性设置为true。

3.异步回调与记录队列

1.记录队列
每次MutationRecord被添加到MutationObserver的记录队列时,仅当之前没有已排期的微任务回调时,才会将观察者注册的回调作为微任务调度到任务队列上。
2.takeRecords()方法
调用MutationObserver实例的takeRecords()方法可以清空记录队列,取出并返回其中的所有MutationRecord实例。

4.性能、内存和垃圾回收

MutationObserver实例与目标节点之间的引用关系是非对称的。MutationObserver拥有对要观察的目标节点的弱引用,所以不会妨碍垃圾回收程序回收目标节点。然而,目标节点拥有对MutationObserver的强引用。如果目标节点从DOM中被移除,随后被垃圾回收,则关联的MutationObserver也会被垃圾回收。

总结

文档对象模型(DOM)是语言中立的HTML和XML文档的API。DOM Level 1将HTML和XML文档定义为一个节点的多层级结构,并暴露出JavaScript接口以操作文档的底层结构和外观。
DOM由一系列节点类型构成,主要包括以下几种:
 Node是基准节点类型,是文档一个部分的抽象表示,所有其他类型都继承Node。
 Document类型表示整个文档,对应树形结构的根节点。在JavaScript中,document对象是Document的实例,拥有查询和获取节点的很多方法。
 Element节点表示文档中所有HTML或XML元素,可以用来操作它们的内容和属性。
 其他节点类型分别表示文本内容、注释、文档类型、CDATA区块和文档片段。
DOM编程在多数情况下没什么问题,在涉及<script>和<style>元素时会有一点兼容性问题。因为这些元素分别包含脚本和样式信息,所有浏览器会将它们与其他元素区别对待。
要理解DOM,最关键的一点是知道影响其性能的问题所在。DOM操作在JavaScript代码中是代价比较高的,NodeList对象尤其要注意。NodeList对象时“实时更新”的,这意味着每次访问它都会执行一次新的查询。考虑到这些问题,实践中要尽量减少DOM操作的数量。
MutationObserver是为代替性能不好的MutationEvent而问世的。使用它可以精准地监控DOM变化,而且API也相对简单。

JS高级程序设计(14)相关推荐

  1. 《Ext JS 高级程序设计》的目录与样张

    第一部分 Ext Core 第1 章 Ext Core 重要概念 ············· 2 1.1 Ext.Element······························· 2 1. ...

  2. Ext JS高级程序设计

    Ext JS高级程序设计 图书详细情况查看: http://www.china-pub.com/193076 市场价 :¥59.00 会员价 : ¥44.25(75折) [作 者]黄灯桥;徐会生 [同 ...

  3. JS高级程序设计——阅读笔记四

    JS高级程序设计--阅读笔记四 第六章 集合引用类型 6.1 Object 6.2 Array 6.2.1 创建数组 from()函数 6.2.2 数组空位 6.2.3 数组索引 6.2.4 检测数组 ...

  4. js高级程序设计第四部分

    js高级程序设计:BOM.客户端检测 BOM window对象 在浏览器中,window 对象有双重角色,它既是通过 JavaScript 访问浏览器窗口的一个接口,又是 ECMAScript 规定的 ...

  5. 读js高级程序设计中有感

    我现在都到js高级程序设计最难的章节,第六章,对象,虽然有人推荐不用全部读完,但我还是完整的读一遍吧,一遍可以敲代码一边看书理解加百度,我认为这种方式很好,可以加深理解增强印象,这是我觉得这本书比JS ...

  6. JS高级程序设计【红宝书】学习笔记——数据类型

    目录 数据类型 Number类型 1.值的范围 2.NaN 3.数值转换 String类型 1.字符字面量(详见JS高级程序设计P63 2.转换为字符串 Symbol类型 Object类型 objec ...

  7. Js高级程序设计第三版学习(十二章)

                                  Js高级程序设计第三版学习(十二章) 第十二章 DOM2和DOM3   1.样式: 访问样式属性 任何支持style特性的HTML元素都有一 ...

  8. JS高级程序设计拾遗

    <JavaScript高级程序设计(第三版)>反反复复看了好多遍了,这次复习作为2017年上半年的最后一次,将所有模糊的.记不清的地方记录下来,方便以后巩固. 0. <script& ...

  9. js高级程序设计(一) —— js简介

    学习<JavaScript高级程序设计>的知识总结,以及对部分内容的扩展~ 1.一个完整的js实现的三个部分 核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM) ...

最新文章

  1. CCS5.5 中报错 Does not match the target type,not loaded 的一种情况
  2. C++ 标准模板库(STL)
  3. Spring Boot + Mybatis多数据源和动态数据源配置
  4. BootLoader与Linux内核的参数传递
  5. c语言switch caseh(op),switch语句求教
  6. android 图片切割代码,Android用clip剪切图像资源
  7. 学计算机的第一年能带电脑吗,大一新生有必要带电脑去学校吗?辅导员给出建议,家长需提前了解...
  8. android module中获取 app_Android模块化中的服务发现机制
  9. 禅道程序员的10条原则--转载--为了不忘
  10. c语言高精度算法阶乘_学了这么久的C语言,原来可以这样解决算法问题...
  11. 如何在MyEclipse 中将工程已经删除的文件恢复过来
  12. linux下RRDTool安装方法
  13. 《软件过程管理》 第一章 软件过程规范
  14. JDK1.8下载、安装和环境配置教程
  15. 本地blast安装使用教程
  16. CRMEB多商户1.7.1版本功能更新预告 预计12月更新
  17. 个人GIT服务器搭建Windows/Linux
  18. python 简单字体解密
  19. Lenient assertions(Unitils介绍三)
  20. D3D9学习笔记之字体

热门文章

  1. springBoot整合mybatis-plus 报错 No qualifying bean of type
  2. 计算机教师继续教育心得,教师继续教育心得体会(精选3篇)
  3. 线路负载及故障检测装置《我搜集的资料》
  4. java求最大公约数_java求最大公约数(分解质因数)
  5. BOA 服务器从-1000开始学习(博客搬运工)
  6. 计算机通信网络扫描版,2015计算机通信与网络作业.pdf
  7. 浅析肖特基二极管如何区别选择及代换
  8. 创宇技能表_[OPEN]知道创宇研发技能表
  9. java 数字翻译成英文_Java实现将数字日期翻译成英文单词的工具类实例
  10. 准备2021年全国大学生数学建模竞赛 固赛助力