在实际使用html时候,经常会被querySelector()、NodeList、HtmlCollection搞混。深入的学习了一下DOM的相关知识。本文中,对曾经的一些疑惑也进行了解答:
- DOM1、DOM2、DOM3都是什么?
- NodeList、HtmlCollection是什么,有什么区别? ...
DOM定义
DOM:Document Object Model,文档对象模型,是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
- DOM1:表示DOM1级规范,为基本的文档结构及查询提供了接口。
IE中,所有的DOM对象都是以COM对象的形式实现的,所以IE中的DOM对象与原生JavaScript对象的行为会不一致。
Node类型
DOM1定义了一个Node接口,JS中所有节点类型都继承自Node类型。
节点拥有的属性:
| 属性类型 | 值 |
|---|---|
| 读取属性 | nodeType、childNodes、firstChild、lastChild、parentNode、nextSibling、previousSibling |
| 操作属性 | appendChild、insertBefore、replaceChild、removeChild、cloneNode |
每个节点都有一个nodeType属性,用于表明节点的类型,一共有12个常量数值表示,我们这里列举常用的两个:
Node.ELEMENT_NODE(1)
Node.ATTRIBUTE_NODE(2)
Node.TEXT_NODE(3)
Node.DOCUMENT_NODE(9)
读取属性:
每个节点都有一个childNodes属性,其中保存着一个NodeList对象。NodeList对象是个类数组对象,是基于DOM结构动态执行查询的结果,因此DOM结构的变化能够自动反映在NodeList对象中。
demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>dom</title>
</head>
<body>
<div id="parent">
parent
<div id="child1">child1</div>
<div id="child2">child2</div>
</div>
<script>
let parentEle = document.getElementById('parent');
console.log(parentEle.nodeType === Node.ELEMENT_NODE) // true
console.log(parentEle.nodeType === 1) // true
// nodeName的值是元素的标签名
console.log(parentEle.nodeName) // DIV 多数时候使用tagName
console.log(parentEle.nodeValue) // null
console.log(parentEle.childNodes[0]) // " parent "
console.log(parentEle.childNodes[0].nodeType) // 3
console.log('parentEle.childNodes', parentEle.childNodes)
console.log('parentEle.firstChild', parentEle.firstChild)
console.log(parentEle.firstChild === parentEle.childNodes[0]) // true
console.log(parentEle.lastChild === parentEle.childNodes[parentEle.childNodes.length - 1]) // true
let child1Ele = document.getElementById('child1');
console.log('child1Ele.parentNode', child1Ele.parentNode)
console.log('child1Ele.nextSibling', child1Ele.nextSibling)
console.log('child1Ele.previousSibling', child1Ele.previousSibling)
</script>
</body>
</html>
操作属性:
<body>
<div id="active">
active
</div>
<div id="parent">
parent
<div id="child1">child1</div>
<div id="child2">child2</div>
</div>
<script>
let parentEle = document.getElementById('parent');
let activeNode = document.getElementById('active');
// appendChild(): 向 childNodes 列表的末尾添加一个节点
const returnedNode = parentEle.appendChild(activeNode);
console.log('returnedNode',returnedNode)
console.log(returnedNode == activeNode) // true
</script>
</body>
观察dom的节点变化,就可以理解为什么说NodeList是动态的。
// 插入后成为最后一个节点
someNode.insertBefore(newNode, null)
// 插入后成为第一个节点
someNode.insertBefore(newNode, firstChild)
<body>
<div id="parent">
parent
<div id="child1">child1</div>
<div id="child2">child2</div>
</div>
<script>
const parentEle = document.getElementById('parent');
const child1Ele = document.getElementById('child1');
const newNode = document.createElement('div');
newNode.setAttribute('id', 'newId');
newNode.innerText = "newText";
console.log(newNode)
const replaceNode = parentEle.replaceChild(newNode, child1Ele)
console.log('replaceNode',replaceNode)
console.log(replaceNode == child1Ele) // true
</script>
</body>
const child2Ele = document.getElementById('child2');
const remodeNode = parentEle.removeChild(child2Ele);
console.log(remodeNode == child1Ele) // true
Document类型
Document类型表示文档。document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面,是window对象的一个属性,可以作为全局对象来访问。
nodeType: 9
nodeName: "#document"
nodeValue: null
parentNode: null
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>dom</title>
</head>
<body>
<script>
console.log(document.documentElement) // 获取html标签
console.log(document.childNodes[0])
console.log(document.childNodes[1] == document.documentElement) // true
console.log(document.body) // 获取body标签
</script>
</body>
</html>
文档信息
获取/修改title:document.title
网页请求:url、domain、referrer;只有domain可以设置,通过设置内嵌页面的domain可以解决跨域问题。在没有来源页面的情况下,referrer可能为空字符串。这些信息都存在于http请求头。
HTMLCollection
DOM节点(node)不光包含HTML元素,还包含text node(字符节点)和comment(注释),既然HTMLCollection只包含HTML元素,那NodeList包含所有类型的DOM节点。
HTMLCollection和NodeList区别:
- NodeList:一个节点的集合,既可以包含元素,又可以包含其他节点(注释节点、文本节点等)
- HTMLCollection:元素的集合,只有Element
// 返回HTMLCollection对象
document.getElementsByTagName()
document.anchors // 所有带name属性的<a>元素
document.links // 所有带href属性的<a>元素
document.forms == document.getElementByTagsName('form')
document.images == document.getElementByTagsName('img')
document.getElementsName()
document.getElementsByClassName()
// 返回NodeList对象
document.getElementById()
document.querySelector()
document.querySelectorAll()
返回HTMLCollection对象和返回NodeList对象的不同demo:
<p id="testp" class="test-p-class">
<span>p content</span>
</p>
<script>
const pLabelEle = document.getElementsByTagName('p');
const pIdEle = document.getElementById('testp')
const pClassEle = document.getElementsByClassName('test-p-class')
console.log('pLabelEle', pLabelEle)
console.log(pLabelEle.childNodes)
console.log('pIdEle', pIdEle)
console.log(pIdEle.childNodes)
console.log('pIdEle', pClassEle)
console.log(pClassEle.childNodes)
</script>
结果:
children属性是HTMLCollection的实例,包含元素中还是元素的子节点,其他和childNodes没什么区别。
<div class="parent">
this is patent content
<div class="child">
this is child content
</div>
<!-- this is comment -->
</div>
<script>
var parent = document.querySelector('.parent');
console.log(parent.childNodes);
console.log(parent.children);
</script>
输出结果:
至于parent即有childNodes属性,又有children属性呢?
因为parent即是一个Node对象(拥有childNodes属性),又因为它有子元素所以它又是一个ParentNode对象(拥有children属性)。
HTMLCollection是比较早期的模型,只能包含HTML元素,早期就有的接口如document.getElementsByClassName, document.getElementsByTagName返回的就是HTMLCollection。NodeList是比较新的模型,相比HTMLCollection更加完善,不光有HTML元素,还有text节点和comment。比较新的接口如document.querySelectorAll返回的就是NodeList。
详解NodeList和HtmlCollection:segmentfault.com/a/119000001…