DOM(文档对象模型)
操作元素
元素、节点、文档
- 元素:有 HTML 标签包裹的节点
- 节点:包括 文本节点、注释节点、元素节点 等
- HTML 中,单独的文本即节点
- 若元素有文本内容,其子节点就是文本节点
- 可以通过
node.nodeType查看具体类型
- 文档:
document对象,指代整个文档流,可以通过document.head、document.body快速访问文档的指定部分
覆写文档
document.write(str): 该方法会复写整个文档
选择元素
document.getElementById(id): 根据 ID 选中匹配的元素docuemnt.getElementsByClassName(class): 根据 class 选中匹配的全部元素document.getElementsByTagName(tag): 根据 tag 选中匹配的全部元素document.querySelector(selector): 根据选择器选中匹配的第一个元素document.querySelectorAll(selector): 根据选择器选中匹配的全部元素document.getElementsByName(name): 根据 name 属性值选中匹配的全部元素
访问元素
创建节点
document.createDocumentFragment(): 创建文档片段,该方法一般用于批量增减元素节点,随后插入文档流中,避免触发多次回流document.createElement(eleName): 创建元素节点document.createTextNode(textContent): 创建文本节点document.createComment(commentContent): 创建注释节点
元素增减
dom.replaceChild(newNode, oldNode): 替换节点dom.insertBefore(newNode, refNode): 在参考节点之前插入子节点dom.append(node1[, node2[... noden]]): 在元素最末子节点后追加 1 或多个 子节点dom.appendChild(node): 在元素最末子节点后追加 子节点dom.remove(): 移除当前节点dom.removeChild(node): 移除元素中的指定 子节点
元素节点
Node.nodeValue: 节点值(一般用不上)- 文本节点: 返回文本内容
- 注释节点: 返回注释内容
- 元素节点: 返回
null
dom.childNodes: 子节点dom.nextSibling: 下一个相邻兄弟节点dom.previousSibling: 上一个相邻兄弟节点dom.children: 子元素dom.nextElementSibling: 下一个相邻兄弟元素dom.previousElementSibling: 上一个相邻兄弟元素dom.firstChild: 第一个子节点dom.firstElementChild: 第一个 子元素dom.lastChild: 最末位子节点dom.lastElementChild: 最末位子元素dom.parentNode: 父节点dom.offsetParents: 离当前元素最近的有定位的父节点,如果都没有定位默认为 body
元素内容
dom.innerHTML: 元素内容(含内嵌元素)dom.innerHTML = newHTML: 覆写元素内容(内嵌元素视为 HTML)dom.textContent: 元素内容(不含内嵌元素,但包括内嵌元素文本)dom.textContent = newContent: 覆写元素内容(内嵌元素视为文本)(若含内嵌元素则会被替换)
尺寸定位(详见 元素尺寸与定位)
dom.clientWidth/dom.clientHeightdom.offsetWidth/dom.offsetHeightdom.scrollWidth/dom.scrollHeightdom.scrollTop/dom.scrollLeftdom.offsetTop/dom/offsetLeft
元素属性
dom.attributes: 元素属性dom.hasAttribute(attrName): 判断是否存在指定属性dom.getAttribute(attrName): 获取元素属性dom.setAttribute(attrName, attrValue): 设置元素属性dom.removeAttribute(attrName): 移除指定属性
类与样式(元素属性的一部分)
dom.style[style] = newVal: 给元素样式赋值(注意,dom.style获取的样式不一定是真实表现的样式)getComputedStyle(dom): 获取元素样式dom.classList: 元素 classdom.classList.add(token1[, token2[, ...tokenN]]): 添加 classdom.classList.remove(token1[, token2[, ...]]): 移除 classdom.classList.replace(oldClass, newClass): 替换 classdom.classList.toggle(className[, force: boolean]): 切换 class(若存在则移除,若不存在则添加)(与元素是否含有对于类名无关), 传递force可强制指定方向
监听元素
可以通过 MutationObserver 监听元素变动 (兼容性 ie11+ (2013+))
ts
type MutationRecordType = "attributes" | "characterData" | "childList";
/**
* 注意
* - attributes" | "characterData" | "childList" 中至少一个为 true
* - characterData 仅作用于 `Text`(document.createTextNode)、`Comment`(document.createComment) 元素
*/
interface MutationObserverInit {
attributes?: boolean; // 是否监控属性变更
attributeFilter?: string[]; // 指定要监控的属性名称,不指定则监控所有属性
attributeOldValue?: boolean; // 是否记录属性变更的前值
characterData?: boolean; // 是否监控指定目标节点或子节点树中节点包含的字符数据变化
characterDataOldValue?: boolean; // 是否记录字符数据前值
childList?: boolean; // 是否监控目标节点添加或删除子节点
subtree?: boolean; // 是否扩大监控范围至目标节点整个节点树的所有节点
}
interface MutationRecord {
readonly addedNodes: NodeList;
readonly attributeName: string | null;
readonly attributeNamespace: string | null;
readonly nextSibling: Node | null;
readonly oldValue: string | null;
readonly previousSibling: Node | null;
readonly removedNodes: NodeList;
readonly target: Node;
readonly type: MutationRecordType;
}
interface MutationCallback {
(mutations: MutationRecord[], observer: MutationObserver): void;
}
interface MutationObserver {
new (callback: MutationCallback): MutationObserver;
prototype: MutationObserver;
disconnect(): void;
observe(target: Node, options?: MutationObserverInit): void;
takeRecords(): MutationRecord[];
}说明
- 相关事件
- DOMAttributeNameChanged
- DOMAttrModified
- DOMCharacterDataModified
- DOMContentLoaded
- DOMElementNameChanged
- DOMNodeInserted
- DOMNodeInsertedIntoDocument
- DOMNodeRemoved
- DOMNodeRemovedFromDocument
- DOMSubtreeModified
.innerText与TextNode在表现上相似,但TextNode可以创建多个,.innerText为一次性修改;因而.innerText触发 'childList'
示例
html
<div id="target"></div>
<button id="btn1">change node</button>
<button id="btn2">change text</button>
<button id="btn3">change attr</button>
<button id="btn4">intercept records</button>
<button id="btn5">stop observe</button>
<script>
const observer = new MutationObserver(function (mutations, observe) {
// observe === observer true
console.log(mutations);
for (const mutation of mutations) {
if (mutation.type === "attributes") { }
if (mutation.type === "childList") { }
if (mutation.type === "characterData") { }
}
});
const target = document.getElementById("target");
const textNode = document.createTextNode("init text");
observer.observe(target, { attributes: true, childList: true, characterData: true, subtree: true });
const btn1 = document.getElementById("btn1");
const btn2 = document.getElementById("btn2");
const btn3 = document.getElementById("btn3");
const btn4 = document.getElementById("btn4");
const btn5 = document.getElementById("btn5");
btn1.addEventListener("click", () => target.appendChild(textNode););
btn2.addEventListener("click", () => textNode.textContent = "text changed" );
btn3.addEventListener("click", () => target.className = "red" );
btn4.addEventListener("click", () => {
target.appendChild(document.createTextNode("some text"));
console.log(
".takeRecords() 可以获取回调 mutations,调用后不触发回调,且不受 .disconnect() 影响",
observer.takeRecords()
);
});
btn5.addEventListener("click", () => observer.disconnect(););
</script>