DOM是什么
文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将web页面和脚本或程序语言连接起来。
简单用一句话讲,JavaScript用来操作网页元素的。
DOM操作
获取元素
window.idxx
doucument.getElementById('idxxx')
doucument.getElementsByTagname('div')[0] //第一个TagName是div的
doucument.getElementsByClassName('red')[0]
doucument.querySelector('#idxxx')
doucument.querySelectorAll('.red')[0]
一般来说,只使用最后两个,只有当需要兼容IE时才使用getElement(s)Byxxx。
获取特定元素
- 获取html元素
doucment.doucumentElement
- 获取head元素
doucument.head
- 获取body元素
document.body
- 获取窗口(窗口不是元素)
window
- 获取所有元素
document.all
,值得注意的时document.all
是一个falsy值
获取元素的原型
通过上面的操作,可以获取到元素,这个元素是一个对象。我们需要搞清它的原型链。
举例说明:
可以通过console.dir(div1)
的方法查看一个div的原型链。
然后先看到的是它的自身属性:className,id,style等
第一层原型:HTMLDivElement.prototype,这是所有div的共有属性。
第二层原型:HTMLElement.prototype,这是所有HTML标签的共有属性。
第三层原型:Element.prototype,这里面是所有XML、HTML标签的共有属性(浏览器不光能展示HTML,也能展示XML)。
第四层原型:Node.prototype,这是所有节点的共有属性。节点包括XML标签文本注释、HTML标签文本注释等等。
第五层原型:EventTarget.prototype,这里面最重要的函数属性是addEvementListener(事件捕获和冒泡相关)
最后一层原型:即Object.prototype
节点与元素的区别
节点包括以下几种(更详细的请查询MDN文档):
通过x.nodeType
得到一个代表节点类型的数字,
- 1表示元素Element,也叫标签Tag
- 3表示文本Test
- 8表示注释Comment
- 9表示文档Docu
- 11表示文档片段DocuFragment
主要需要记忆一和二两条。
节点的增删改查
增
创建一个标签节点
let div1 = document.createElement('div')
doucument.createElement('style')
doucument.createElement('script')
doucument.createElement('li')
创建一个文本节点
text1 = document.createTestNode('你好')
标签里面插入文本
div1.appendChild(text1)
div1.innerTest = '你好'
div1.textContent = '你好' //不能使用div1.appendChild('你好')
如何将节点插入页面中
创建的标签默认处于JS线程中,需要把其查到head或body里面,才会生效
document.boy.appendChild(div)
或者已在页面的元素.appendChild(div)
appendChild
如果同时将一个节点插入两个地方,节点会出现在代码最后插入的位置,一个元素崩同时出现在两个地方,除非复制一份。
删
两种方法:
旧:parentNode.removeChild(childNode)
新:childNode.remove()
改
写标准属性
改class:div.calssName = 'red blue' //全覆盖,所有该class都将遭到修改
改class:div.classList.add('red') //追加class'red'
改style:div.style = 'width:100px;color:blue;'
改style的一部分:div.style.width = '200px'
注意大小写:div.style.backgroundColor = 'white'
改data-*属性:div.dataset.x = 'frank'
读标准属性
div.classList/a.herf //某些浏览器可能会加工改变最后结果,一般来说两种都可以
div.getAttribute('class')/a.getAttribute('herf')
改事件处理函数
举例说明:
div.onclick
默认为null,将div.onclik
修改为一个函数fn。
当当点击div时,浏览器就会调用该函数。fn.call(div,evevt)
div当作this,event包含点击事件的所有信息。
改文本内容
div.innerTest = 'xxx'
div.textContent = 'xxx'
两者几乎没有区别
改HTML内容
div.innerHTML='<strong>重要内容</strong>'
改标签
div.innerHTML = '' //先清空
div.appendChild(div2) //再加内容
改父元素
newParent.appendChild(div)
查
查父元素
node.parentNode
或者node.parentElement
查父元素的父元素
node.parentNode.parentNode
查子代
node.childNode
或者node.children
优先使用后者
当子代变化时,两者会实时变化,如果不需要变化可以使用document.querySelectorAll()
查同级元素
node.parentNode.childNodes
需要排除自己
node.parentNode.children
需要排除自己
查第一个子元素
node.firstChild
查看最后一个子元素
node.lastChild
查看上一个同级元素
node.previousSibling
查看下一个同级元素
node.nextSibling
注意:最后两个在查询过程中可能会查到文本节点,可以改为node.previousSibling
遍历元素
travel1 = (node,fn) =>{
fn(node)
if(node.children){
for(let i=0;i<node.children.length;i++){
travel(node.children[i],fn)
}
}
}
travel(div1,(node)=>console.log(node))
DOM的操作是跨线程的
浏览器中有JS引擎,渲染引擎,分为不同线程。
各线程各司其职,JS引擎只能操作JS而不能操作页面,渲染引擎只能操作页面不能操作JS。
跨线程通信
document.body.appendChild(div1)
当浏览器发现JS在body里面加了一个div1对象,就会通知渲染引擎在页面里也新增一个div元素,新增的div元素所有属性都照抄div1对象。
- 在div1放入页面之前,所有对div1的操作都属于JS线程内的操作
- 在div1放入页面之时,浏览器发现JS的意图,通知渲染线程在页面中渲染div1对应的元素
- 在div1放入页面之后,对div1的操作都有可能会触发重新渲染。
DOM的属性同步
- 当对div1的标准属性进行修改,会被浏览器同步到页面上,例如id,className,title等
- data-*属性同标准属性
- 非标准属性的修改只会停留在JS线程中,不会同步到页面里
Property与Attribute
property属性
JS线程中div1的所有属性,叫做div1的property
attribute也是属性
渲染引擎中div1对应标签的属性,叫做attribute
区别
大部分时候,同名的property和attribute值相等
但如果不是标准属性,那么它俩只会在一开始时相等
但注意attribute只支持字符串
而property支持字符串、布尔等类型