DOM编程

123 阅读6分钟

目录

DOM概览

DOM获取元素

DOM获取到的元素的原型

DOM元素的增删改查

DOM操作跨线程原理

一、DOM概览

网页其实是一棵树

  • JS如何操作这棵树

    1. 浏览器往window上加一个document即可

    2. JS用document操作网页

    3. 这就是Document Object Model 文档对象模型 简称:DOM

学习了DOM后,体会到的一个道理

  • DOM很难用,我一定要封装DOM

二、获取元素

1.获取元素

  • 可以通过很多API获取元素

    1. window.idxxx 或直接idxxx 如果id和全局属性冲突 这个方法就不能用

    2. document.getElementById('idxxx')

    3. document.getElementsByTagName('div')[0] 找到所有div标签,可以输入下标确定具体哪一个

    4. document.getElementsByClassName('red')[0] 找到所有类名为red的标签

    5. document.queryselector('#idxxx') 要加#号

    6. document.queryselectorAll('.red')[0]

  • 如果不考虑兼容性就用document.queryselectordocument.queryselectorAll

  • 如果考虑兼容IE就只能用getElement(s)Byxxx

2. 获取特定元素

  • 获取html标签

    document.documentElement

  • 获取head元素

    document.head

  • 获取body元素

    document.body

  • 获取窗口(窗口并不是一个元素)

    window

  • 获取所有元素

    1. document.all 这是IE发明的属性 但所有浏览器都能用

    2. 这个document.all是第六个falsy值

三、DOM获取到的元素的原型

  • DOM获取到的元素其实是对象,这里要搞清楚它的原型,这个很重要

  • console.dir(div) 查看获取到的div的原型链

    1. chrome显示的原型的名字可能有些问题

    2. 自身属性:classNameidstyle等等属性

    3. 第一层原型 HTMLDivElement.Prototype 里面是所有div的共有属性,不用特别细看

    4. 第二层原型 HTMLElement.prototype 里面是HTML标签的共有属性,不用特别细看

    5. 第三层原型 Element.prototype 里面是所有XML、HTML的共有属性,因为浏览器不止可以显示HTML,还有其他类型也能显示

    6. 第四层原型 Node.prototype 里面是所有节点的共有属性,包括XML、HTML标签文本注释等属性

    7. 第五层原型 EventTarget.prototype 里面最重要的函数属性addEventListener 后面详细学习

    8. 最后一层原型 Object.prototype 根对象

节点Node 包括

  • MDN有完整描述,x.nodeType得到一个数字

    • 1 表示元素 Element 也叫元素标签 Tag

    • 3 表示文本 Text

    • 8 表示注释 comment

    • 9 表示文档 Document

    • 11 表示文档片段 DocumentFragment

    • 重点记住1和2是什么

四、元素的增删改查

1. 增

创建一个节点

  • let div1 = document.createElement('div')

  • document.createElement('style')

  • document.createElement('script')

  • document.createElement('li')

创建一个文本节点

  • text1 = document.createTextNode('你好')

标签里面插入文本

  • div1.appendChild(text1)

  • div1.innerText = '你好'或者div1.textContent = '你好'

  • 但是不能用div1.appendChild('你好')

插入到页面中

  • 创建的标签默认处于JS线程中

  • 必须把它插入到head或者body里,它才会在页面中生效

  • document.body.appendchild(div)

  • 或者已在页面中的元素.appendChild(div)

appendchild解析

  • 代码
//页面中有div#test1和div#test2

let div = document.createElment('div')
test1.appendchild('div')
test2.appendchild('div2')
  • 最终这个div只会出现在test2里,因为一个元素不能出现在两个地方,除非复制一份

复制节点

  • let div2 = div.cloneNode()

  • 如果括号为空 那么默认是浅拷贝,如果括号内为true 那就是深拷贝

2. 删

方法1 (旧)

parentNode.childChild(childNode)

方法2 (新)

childNode.remove() 不兼容IE

注意

  • 如果一个Node被移出页面(DOM树),是可以被恢复到页面中的,因为这个Node在内存里还存在,除非把内存对应的Node也移除就彻底回不来了

3.改

写标准属性

  • classdiv.className = 'rad blue' 这个方法会把该Node的所有class全部覆盖掉

  • classdiv.classList.add('rad')

  • stylediv.style = 'width: 100px; color:blue'

  • style的一部分: div.style.width = '200px'

  • 注意区分大小写: div.style.backgroundColor = 'skybule'

  • data-*属性: div.dataset.x = 'jack'

读标准属性

div.classList/a.href 这样读属性可能会有问题

div.getAttribute('class')/a.getAttribute('href') 这种方法最稳妥

改事件处理函数

  • div.onclick 默认为null

    1. 默认点击div不会有任何事情发生

    2. 但是如果把div.onclick改为一个函数fn

    3. 那么点击div的时候,浏览器就会调用这个函数

    4. 调用方式是fn.call(div,event) div被当做this

    5. event则包含了点击事件的所有信息,如坐标等

    6. div.addEventListener 后面详细学习,是div.onclick的升级版

改文本内容

  • div.innerText = 'xxx'

  • div.textContent = xxx

  • 两者几乎没有区别

改HTML内容

  • div.innerHTML = '<strong>重要内容</strong>'

改标签

  • div.innerHTML = '' 先清空

  • div.appendchild(div2) 再加内容

改父级元素

  • 找一个新爹

    1. newParent.appendchild(div)

    2. 直接这样,会让想改的元素在原来的地方消失

4.查

查父级元素

  • node.parentNodenode.parentElement

查父级元素的父级元素

  • node.parentNode.parentNode

查子代元素

  • Node.childrennode.childNodes 用第一种居多,两种都会实时更新

查兄弟姐妹元素

  • node.parentNode.childNodes

  • node.parentNode.childen 用这种就可以不用再排除文本节点,方便省事

  • 两种方法在选中父级元素后都要排除掉自己(egg 疼)

查第一个子代

  • node.firschild

查最后一个子代

  • node.lastchild

查上下一个兄弟元素

  • node.previousSibling 上一个元素

  • node.nextSibling 下一个元素

  • 如果只想查看元素就在Sibling前加Element

遍历一个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操作跨线程原理

  • 概念: DON操作是跨线程操作

  • 浏览器分为渲染引擎和JS引擎等

f59c868aa1fef5ef0a9531fea565690.png

跨线程操作

  • 各线程各司其职

    1. JS引擎不能操作页面,只能操作JS

    2. 渲染引擎不能操作JS,只能操作页面

    3. document.body.appendchild(div1)

    4. 这个JS代码是如何改变页面的

  • 跨线程通信

    1. 当浏览器发现JS在body里加了一个div1对象

    2. 浏览器会通知渲染引擎在页面里也新增一个div元素

    3. 新增的div元素所有属性照抄div1对象的属性

插入新标签的完整过程

  • div1放入页面之前

    1. 我们对div1所有的操作都属于JS线程内的操作,是不会直接影响到页面的
  • div1放入页面时

    1. 浏览器发现了JS的意图

    2. 浏览器通知渲染线程在页面中渲染div1对应的元素

  • div1放入页面之后

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

    2. div1.id = 'newId' 可能会重新渲染也可能不会,这取决于div1id有没有让浏览器渲染的属性

    3. div1.title = 'new' 可能会重新渲染也可能不会,理由同上

    4. 如果对div1连续进行多次操作,浏览器可能会合并成一次操作,也可能不会,假如在多次操作的中间,没有改变要求浏览器进行渲染的属性,就会合并成一次操作

属性同步

  • 标准属性和data-*属性

    1. 对div1的标准属性和data-*属性进行修改,会被浏览器同步到页面中

    2. 比如id/className/title

  • 非标准属性

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

    2. 比如x属性(给标签新建一个x属性,修改x属性就不会被同步到页面里)

  • 启示

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

    6482258fc22c972659928225c000f66.png

PropertyAttribute

  • Property属性

    1. JS线程中div1的所有属性,叫做div1property
  • Attribute也是属性

    1. 渲染引擎中div1对应标签的属性,叫做Attribute
  • 区别

    1. 大部分时候,同名的PropertyAttribute的值相等

    2. 但如果不是标准属性,那他们只在一开始时相等

    3. Attribute只支持字符串,Property支持字符串、布尔等类型