DOM知识总结

84 阅读4分钟

网页其实是一棵树

文字也是节点

image-20221205082110634

JS如何操作网页 >> 浏览器往window上加了document对象

image-20221205082004189

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

image-20221205083812424

获取head

document.head

获取body

document.body

获取窗口

window

获取所有元素

注意和获取html区分

document.all是伪数组,而且是falsy值(早期利用该值检测当前页面是否为IE)

image-20221205083830632

div原型

获取到的是对象

console.dir(document.getElementsByTagName(‘div’)[9])

6层原型 记忆

div原型链

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’)

image-20221205092524099

插入页面

创建标签默认在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()

  1. Node 找爸爸删儿子 div1.parentNode.removeChild(div1)
  2. 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对象

插入新标签完整过程

  1. div1放入页面之前

    对div1所有操作都属于JS线程内的操作

  2. 把div1放入页面之时

    浏览器通知渲染线程

  3. 把div1放入页面之后

    对div1的操作都有可能会触发重新渲染

    改id可能会触发重新渲染

    改title也可能会触发重新渲染

    多次操作, 浏览器可能会合并成一次 代码

属性同步

标准属性

对标注属性的修改,会被浏览器同步到页面中

data-*属性

同上

非标准属性

对非标准属性的修改只会停留在JS线程中,不会同步到页面里

如果有自定义属性,又想被同步到页面中,要使用data-作为前缀

Property vs Attribute

Property是属性

JS线程中div1的所有属性,叫做div1的property

Attribute也是属性

渲染引擎中div1对应标签的属性,叫做attribute

区别

大部分时候,同名的property和attribute值相等,但如果是非标准属性,则只会在一开始时相等

attribute只支持字符串

property支持字符串、布尔等类型