网页其实是一棵树
文字也是节点
JS如何操作网页 >> 浏览器往window上加了document对象
JS用document操作网页,这就是DOM文档对象模型
获取元素/标签
- API
- window.idxxx 或直接 idxxx
- 与window属性同名,用 document.getElementById(‘idxxx’)
- 兼容IE
- document.getElementsByTagName(‘div’)[0]
- document.getElementsByClassName(‘red’)[0]
- 常用
- document.querySelector(selector)和document.querySelectorAll(selector)[0]
获取特定元素
获取html标签
document.documentElement
获取head
document.head
获取body
document.body
获取窗口
window
获取所有元素
注意和获取html区分
document.all是伪数组,而且是falsy值(早期利用该值检测当前页面是否为IE)
div原型
获取到的是对象
console.dir(document.getElementsByTagName(‘div’)[9])
6层原型 记忆
Node和Element傻傻分不清
Node是节点,Element是元素/标签
x.nodeType得到一个数字
1 表示元素 Element,也叫标签 Tag
3表示文本 Text
9表示文档Document
…
节点的增删改查
增
JS创建
创建标签节点
let div1 = document.createElement(‘div’)
创建文本节点
text1 = document.createTextNode(‘hi’)
标签里面插入文本
div1.appendChild(text1) — 这是Node的方法
div.innerText = ‘hi’ 或 div.textContent = ‘hi’ — 这是Element的方法
不能写成div1.appendChild(‘hi’)
插入页面
创建标签默认在JS线程里面,需要插到head或者body里面
document.body.appendChild(div)
或
找一个已经在页面中的元素.appendChild(div)
appendChild
Q: 创建一个div节点,页面中有div#test1和div#test2,执行test1.appendChild(div)和test2.appendChild(div),最终div出现在哪里
A:test2,因为一个元素不能出现在两个地方,除非复制一份let div2 = div1.cloneNode()
删
- Node 找爸爸删儿子
div1.parentNode.removeChild(div1) - Element 自己删自己
div1.remove()
会返回一个oldNode,如果一个node被移出页面(DOM树),可以再次回到页面中
改
改属性
写标准属性
改class
全覆盖 div.className = ‘red blue’
增加 div.classList.add(‘red’)
改style
全覆盖 div.style = ‘xxx:yyy; mmm:nnn;’
改部分 div.style.color = ‘red’ 大小写 div.style.backgroundColor = ‘white’
改自定义属性 div.dataset.x = ‘xxxx’
data-* setAttribute(‘data-x’,’test’) getAttribute(‘data-x’)
读标准属性
div.classList / a.href
div.getAttribute(‘class’) / a.getAttribute(‘href’) 更保险
值可能会不同
改事件处理函数
事件处理函数
div.onclick默认为null, 点击div不会有任何事情发生
此时把div.onclick改为一个函数fn, 点击div浏览器就会调用这个函数
fn.call(div,event)
div被当做this,event包含了点击事件的所有信息, 如坐标
div.addEventListener 升级版
改内容
改文本内容
div.innerText = '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.childNodes 或 node.children 当子代变化时, 会实时变化
node.firstChild
node.lastChild
但是querySelectorAll不会根据页面变化去实时改变自己
查兄弟姐妹
node.parentNode.childNodes / node.parentElement.children
排除自己
let siblings = []
let c = div.parentElement.children
for(let i = 0; i < c.length; i++){
if(c[i] !== div){
siblings.push(c[i])
}
}
node.previousSibling
node.nextSibling
遍历一个div里面的所有元素
travel = (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操作是跨线程的
为什么DOM操作比较慢
浏览器 分为 渲染引擎和JS引擎, 各司其职
但是 document.body.appendChild(div1)是如何改变页面的?
跨线程通信
浏览器发现JS在body里面加了div1对象, 浏览器就会通知渲染引擎在页面里也新增一个div元素, 新增的div元素所有属性都照抄div1对象
插入新标签完整过程
-
div1放入页面之前
对div1所有操作都属于JS线程内的操作
-
把div1放入页面之时
浏览器通知渲染线程
-
把div1放入页面之后
对div1的操作都有可能会触发重新渲染
改id可能会触发重新渲染
改title也可能会触发重新渲染
多次操作, 浏览器可能会合并成一次 代码
属性同步
标准属性
对标注属性的修改,会被浏览器同步到页面中
data-*属性
同上
非标准属性
对非标准属性的修改只会停留在JS线程中,不会同步到页面里
如果有自定义属性,又想被同步到页面中,要使用data-作为前缀
Property vs Attribute
Property是属性
JS线程中div1的所有属性,叫做div1的property
Attribute也是属性
渲染引擎中div1对应标签的属性,叫做attribute
区别
大部分时候,同名的property和attribute值相等,但如果是非标准属性,则只会在一开始时相等
attribute只支持字符串
property支持字符串、布尔等类型