什么是DOM
- 网页其实是一颗树
- 在浏览器window上加一个dicument即可用JS操作这棵树
- window.dicument可以得到整个网页的根节点
- 这就是Document Object Model对象模型 (DOM)
- 把网页抽象成一个document对象并对它进行操作的这种方式叫做DOM
获取页面元素,也叫获取标签
通过id来获取
有很多 API
window.idxxx或直接idxxx (id不能和全局属性冲突)
document.getElementByld('id')
document.getElementsByTagName('div')[0]
document.getElementsByClassName('red')[0]
document.querySelector('#id')
document.querySelectorAll(.red')[0]
常用
document.querySelector('#id') 和 document.querySelectorAll('#id')[0]
做 demo 可以直接用 id
获取特定元素的根元素
获取 html 元素 document.documentElement
获取 head 元素 document.head
获取 body 元素 document.body
获取窗口 (窗口不是元素) window
获取所有元素 document.all
这个 document.all 是个奇葩,第6个 falsy 值
可以区分IE浏览器和其它浏览器
if(document.all){console.log('ie浏览器')}else{console.log('其他浏览器')}
div完整原型链 图示
- 获取的元素是一个对象并有六层原型
- consoe.dir(div) 打印div对象的结构
节点的增删改查
增
创建一个标签节点let div1 = document.createElement('div')
document.createElement('style')
document.createElement('script')
document.createElement('li')
创建一个文本节点
text1 = document.createTextNode('你好')
文本插入标签里面
divl.appendChild(text1)
divl.innerText ='你好' 或者 divl.textContent ='你好'
但是不能用 divl.appendChild('你好')
例:
let div1 = document.createElement('div') //创建标签节点
text = document.createTextNode('你好') //创建文本节点
div1.appendChild(text) //文本插入标签
document.body.appendChild(div1) //标签节点插入body
div1.style.position = 'fixed' //浮到顶层
div1.style.top = 0
div1.style.left = 0
<div style="position: fixed; top: 0px; left: 0px;">你好</div>
(续)
- 插入页面中
- 你创建的标签默认处于 JS 线程中
- 你必须把它插到 head 或者 body 里面,它才会生
- document.body.appendChild(div)
- 或者
- 已在页面中的元素.appendChild(div)
- 但一个元素不能出现在两个地方,除非复制一份
let div2 = div1.cloneNode(true) //深拷贝 这样就可以放两个地方
删
两种方法
旧方法: parentNode.childChild(childNode)
新方法: childNode.remove() //不兼容IE
node 只是被移出页面(DOM树),还存在于内存中,它还可以再次回到页面中
令 div = null 即可彻底删除
例:
div1.remove()
div2.remove()
再次把它插入head和body里即可回来
改
改标准属性
改 class:div.className = 'red blue' //全覆盖
改 class:div.classList.add('red') //保留之前并补充
改 style: div.style = 'width: 100px; color: blue;' //style全覆盖
改 style 的一部分: div.style.width ='200px'
大小写: div.style.backgroundColor= 'white'
改 data-* 属性: div.dataset.x = 'frank'
读标准属性
div.classList/ a.href //获取的href可能会被浏览器加工
div.getAttribute('class') / a.getAttribute('href') //这种不会
两种方法都可以,但值可能稍微有些不同
改事件处理函数
div.onclick 默认为 null
默认点击 div 不会有任何事情发生
但是如果你把 div.onclick 改为一个函数 fn
那么点击 div 的时候,浏览器就会调用这个函数
并且是这样调用的 fn.call(div, event)
div 会被当做 this
event 则包含了点击事件的所有信息,如坐标
例:
div1.onclick = ()=>{console.log('hi')}
div.addEventListener是 div.onclick 的升级版(可以写无数个函数)
改内容
改文本内容
div.innerText = 'xxx'
div.textContent = 'xxx' //两者几乎没有区别
改 HTML 内容
div.innerHTML='<strong>重要内容</strong>'
改标签
div.innerHTML=" // 先清空
div.appendChild(div2) // 再加内容
查
查爸爸
node.parentNode 或者 node.parentElement
查爷爷
node.parentNode.parentNode
查子代
node.childNodes //包括文本节点
node.children //不包括文本节点 优先使用
//当子代变化时,两者也会实时更新
//用document.queryselectorAll()查询时,不会实时更新
查兄弟姐妹
node.parentNode.childNodes 还要排除自己
node.parentNode.children 还有排除自己 //优先使用
例:查兄弟姐妹
let brother = []
let c = div1.parentElement.children
for(i=0;i<c.length;i++){
if(c[i] !== div1){
brother.push(c[i])
}
}
查看老大 node.firstChild
查看老小 node.lastChild
查看上一个哥哥/姐姐 node.previousSibling //node.previousElementSibling排除文本节点
查看下一个弟弟/妹妹node.nextSibling //node.nextElementSibling排除文本节点
DOM跨线程操作
浏览器有两个重要功能:渲染引擎和JS引擎,两个引擎在不同的线程互不打扰
跨线程操作
各线程各司其职
- JS 引擎不能操作页面,只能操作 JS
- 渲染引擎不能操作JS,只能操作页面
- document.body.appendChild(div1)
- 这句 JS 是如何改变页面的?
跨线程通信
- 当浏览器发现 JS 在 body 里面加了个 div1 对象
- 浏览器就会通知渲染引擎在页面里也新增一个 div 元素
- 新增的 div 元素所有属性都照抄 div1 对象
跨线程操作图示
插入新标签的完整过程
- 在 div1 放入页面之前
- 你对 div1 所有的操作都属于 JS 线程内的操作
- 把 div1 放入页面之时
- 浏览器会发现 JS 的意图就会通知渲染线程在页面中渲染 div1 对应的元素
- 把 div1 放入页面之后
- 你对 div1 的操作都有可能会触发重新渲染
- divl.id ='newld'可能会重新渲染,也可能不会
- div1.title ='new' 可能会重新染,也可能不会
- 如果你连续对 div1 多次操作,浏览器可能会合并成一次操作,也可能不会
奇葩案例:
属性同步
标准属性和data属性
- 对 div1 的标准属性的修改,会被浏览器同步到页面中
- 比如 id、className、title 等
非标准属性
- 对非标准属性的修改,则只会停留在 JS 线程中
- 不会同步到页面里
- 比如 x 属性,示例代码
启示 : 如果你有自定义属性,又想被同步到页面中,请使用data- 作为前级
例子
图解
Property vs Attribute
property 属性
- JS 线程中 div1 的所有属性,叫做 div1 的 property
attribute 也是属性
- 渲染引擎中 div1 对应标签的属性,叫做 attribute
区别
- 大部分时候,同名的 property 和 attribute 值相等
- 但如果不是标准属性,那么它俩只会在一开始时相等
- 但注意 attribute 只支持字符串
- 而 property 支持字符串、布尔等类型