DOM
DOM
1. 节点层级
document节点表示每个文档的根节点,根节点的唯一子节点是<html>元素,称为文档元素(documentElement)。
1.1 Node类型
每个节点都有nodeType属性,表示该节点的类型。节点类型由定义在Node类型上的12个数值常量
if (someNode.nodeType == Node.ELEMENT_NODE){
alert("Node is an element.");
}
-
nodeName、nodeValue
if (someNode.nodeType == 1){ value = someNode.nodeName; // 会显示元素的标签名 } // 元素节点的nodeValue属性为null
-
节点关系
每个节点都有一个childNodes属性,其中包含一个NodeList的实例。NodeList会根据DOM结构实时更新。
let firstChild = someNode.childNodes[0]; let secondChild = someNode.childNodes.item(1); //支持item方法 let count = someNode.childNodes.length; //转换为数组 let arrayOfNodes = Array.prototype.slice.call(someNode.childNodes, 0); //或 let arrayOfNodes = Array.from(someNode.childNodes);
每个节点都有一个parentNode属性,指向其DOM树中的父元素。使用previousSibling和 nextSibling可以在胞亲之间导航。
父节点和它的第一个及最后一个子节点也有专门属性: firstChild和lastChild分别指向childNodes中的 第一个和最后一个子节点。
最后还有一个所有节点都共享的关系。ownerDocument 属性是一个指向代表整个文档的文档节点的指针。
-
操纵节点
appendChild() 用于在childNodes列表末尾添加节点,方法返回新添加的节点。
如果节点时已经存在节点,则会移动节点。一个节点不会在文档中同时出现在两个或更多个地方。
insertBefore() 可以将节点插入到特定位置:
// 第二个参数传入null时与appendChild()相同 returnedNode = someNode.insertBefore(newNode, null); alert(newNode == someNode.lastChild); //true // 作为新的第一个子节点插入 returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
replaceChild() 接收两个参数:要插入的节点和要替换的节点
// 替换第一个子节点 let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
removeChild() 用于移除节点
// 删除第一个子节点 let formerFirstChild = someNode.removeChild(someNode.firstChild);
-
其他方法
cloneNode() 复制节点,如果传入true则还会复制内部子节点(不会复制事件处理程序)
let deepList = myList.cloneNode(true); alert(deepList.childNodes.length);
normalize() 用于处理文本节点,后面讨论。
1.2 Document类型
属性 | 描述 |
---|---|
document.documentElement | 始终指向HTML页面中的<html>元素 |
document.body | 直接指向<body>元素 |
document.doctype | 指向<!doctype>标签 |
document.title | 可以修改标题栏,但不会改变<title>标签中的内容 |
document.referrer | 获取链接到当前页面的那个页面的URL |
-
定位元素
方法 描述 getElementById() 接收元素id,如果找到返回元素,没找到返回null,多个则返回第一个 getElementsByTagName() 接收一个参数,要获取元素的标签名,返回包含零个或多个元素的NodeList。 getElementsByName() 根据name属性返回元素NodeList 常用于表单 // <img src="myimage.gif" name="myImage"> let images = document.getElementsByTagName("img"); let myImage = images["myImage"]; //通过name获取
-
特殊集合
document.forms:所有表单
document.images:所有图片
document.links:所有链接
1.3 Element类型
可以通过nodeName或tagName来获取标签名(大写)
if (element.tagName.toLowerCase() == "div"){
//推荐,适用于所有文档
}
-
HTML元素
有5个标准属性,id,dir(文字方向),lang,title(鼠标放置后显示),className(相当于class,class为关键字不可以使用)
<div id="myDiv" class="bd" title="Bodytext" lang="en" dir="ltr"></div>
这些都有对应的属性值可以修改
let div = document.getElementById("myDiv"); alert(div.id); // "myDiv" alert(div.className); // "bd" div.id = "someOtherId"; div.className = "ft";
-
取得属性
getAttribute()、setAttribute()和 removeAttribute()。
let div = document.getElementById("myDiv"); alert(div.getAttribute("id")); //"myDiv" alert(div.getAttribute("class")); //"bd" // 这里要注意class而不是className要填真实名称
根据html5 的要求自定义属性要以data-开头
除了五个标准属性还有其他公认(非自定义)属性也是DOM对象属性(如align)
div.align='left';
getAttribute()和DOM属性访问的两个差异
-
style属性
getAttribute()访问style属性时,返回的是CSS字符串。
通过DOM对象的属性访问时,style属性返回的是一个(CSSStyleDeclaration)对象。DOM对象的style属性用于以编程方式读写元素样式,因此不会直接映射为元素中style属性的字符串值。
-
所有事件属性
当在元素上使用事件属性时(比如 onclick),属性的值是一段JavaScript代码。
使用 getAttribute()访问事件属性,则返回的是字符串形式的源代码,(标签上未设置则为null)
通过DOM对象的属性访问事件属性时返回的则是一个JavaScript函数(未指定该属性则返回 null)
-
-
设置属性
setAttribute() 可以设置html属性和自定义属性
也可以通过DOM属性设置元素属性:
div.id = "someOtherId"; // DOM对象上的自定义属性,不会成为元素的属性 div.mycolor = "red"; alert(div.getAttribute("mycolor")); //null(IE除外)
-
创建元素
document.createElement()方法创建新元素。这个方法接收一个参数,即要创建元素的标签名。
1.4 Text类型
Text节点中包含的文本可以通过nodeValue、data、textContext属性访问
// 输出为"Some <strong>other</strong> message"
div.firstChild.nodeValue = "Some<strong>other</strong> message";
//HTML代码,即小于号、大于号或引号会被转义
-
创建节点
document.createTextNode()
let textNode = document.createTextNode("<strong>Hello</strong> world!");
-
规范化
normalize() 在父节点上调用可以合并相邻全部的文本节点
2. MutationObserver接口
使用MutationObserver可以观察整个文档、DOM树的一部分,或某个元素。此外还可以观察元素属性、子节点、文本,或者前三者任意组合的变化。
2.1 基本用法
初始化并传入一个回调函数:
let observer = new MutationObserver(() => console.log('DOM was mutated!'));
-
observe() 方法
MutationObserver实例不会关联DOM的任何部分。要把这个observer与DOM关联起来,需要使用observe()方法。这个方法接收两个必需的参数:要观察其变化的DOM节点,以及一个MutationObserverInit对象。
MutationObserverInit对象用于控制观察哪些方面的变化,是一个键/值对形式配置选项的字典。
例如:观察<body>元素上的属性变化
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 //异步触发回调函数
-
回调与MutationRecord
回调函数会接收一个MutationRecord对象,记录变化信息
let observer = new MutationObserver((mutationRecords) => console.log(mutationRecords));
-
disconnect()提前终止回调
会断开与DOM之间的监视,未调用的回调不会执行
document.body.className = 'foo'; observer.disconnect(); document.body.className = 'bar'; //(没有日志输出)
因为回调是异步的所以所有回调都会取消
document.body.className = 'foo'; setTimeout(() => { observer.disconnect(); document.body.className = 'bar'; }, 0); // <body> attributes changed // 这样可以执行部分
4.复用MutationObserver
// 观察两个子节点 observer.observe(childA, { attributes: true }); observer.observe(childB, { attributes: true }); // 修改两个子节点的属性 childA.setAttribute('foo', 'bar'); childB.setAttribute('foo', 'bar'); // [<div>, <span>]
5.重用MutationObserver
调用disconnect()并不会结束MutationObserver的生命。重新observe()绑定可以继续使用
2.2 观察范围
设置 | 描述 |
---|---|
attributes: true | 属性变化 |
attributeFilter: ['属性1','属性2'] | 指定观察的属性 |
attributeOldValue: true | 记录原属性值,保存在oldValue中 |
characterData: true | 观察文本节点变化 |
characterDataOldValue: true | 记录原属字符数据 |
childList: true | 观察目标节点子节点的添加和移除 |
subtree: true | 所有后代节点 |