DOM编程

84 阅读5分钟

什么是DOM

  • 网页其实是一颗树
  • 在浏览器window上加一个dicument即可用JS操作这棵树
  • window.dicument可以得到整个网页的根节点
  • 这就是Document Object Model对象模型 (DOM)
  • 把网页抽象成一个document对象并对它进行操作的这种方式叫做DOM

image.png

获取页面元素,也叫获取标签

通过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完整原型链 图示

image.png

  • 获取的元素是一个对象并有六层原型
  • 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.addEventListenerdiv.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 对象

跨线程操作图示 image.png

插入新标签的完整过程

  • 在 div1 放入页面之前
  • 你对 div1 所有的操作都属于 JS 线程内的操作
  • 把 div1 放入页面之时
  • 浏览器会发现 JS 的意图就会通知渲染线程在页面中渲染 div1 对应的元素
  • 把 div1 放入页面之后
  • 你对 div1 的操作都有可能会触发重新渲染
  • divl.id ='newld'可能会重新渲染,也可能不会
  • div1.title ='new' 可能会重新染,也可能不会
  • 如果你连续对 div1 多次操作,浏览器可能会合并成一次操作,也可能不会

奇葩案例: image.png

属性同步

标准属性和data属性

  • 对 div1 的标准属性的修改,会被浏览器同步到页面中
  • 比如 id、className、title 等

非标准属性

  • 对非标准属性的修改,则只会停留在 JS 线程中
  • 不会同步到页面里
  • 比如 x 属性,示例代码

启示 : 如果你有自定义属性,又想被同步到页面中,请使用data- 作为前级

例子 image.png 图解 image.png

Property vs Attribute

property 属性

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

attribute 也是属性

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

区别

  • 大部分时候,同名的 property 和 attribute 值相等
  • 但如果不是标准属性,那么它俩只会在一开始时相等
  • 但注意 attribute 只支持字符串
  • 而 property 支持字符串、布尔等类型