DOM是什么

文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将web页面和脚本或程序语言连接起来。

简单用一句话讲,JavaScript用来操作网页元素的。

DOM操作

获取元素

  1. window.idxx
  2. doucument.getElementById('idxxx')
  3. doucument.getElementsByTagname('div')[0] //第一个TagName是div的
  4. doucument.getElementsByClassName('red')[0]
  5. doucument.querySelector('#idxxx')
  6. doucument.querySelectorAll('.red')[0]

一般来说,只使用最后两个,只有当需要兼容IE时才使用getElement(s)Byxxx。

获取特定元素

  • 获取html元素doucment.doucumentElement
  • 获取head元素doucument.head
  • 获取body元素document.body
  • 获取窗口(窗口不是元素)window
  • 获取所有元素document.all,值得注意的时document.all是一个falsy值

获取元素的原型

通过上面的操作,可以获取到元素,这个元素是一个对象。我们需要搞清它的原型链。

举例说明:

可以通过console.dir(div1)的方法查看一个div的原型链。

然后先看到的是它的自身属性:className,id,style等

第一层原型:HTMLDivElement.prototype,这是所有div的共有属性。

第二层原型:HTMLElement.prototype,这是所有HTML标签的共有属性。

第三层原型:Element.prototype,这里面是所有XML、HTML标签的共有属性(浏览器不光能展示HTML,也能展示XML)。

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

第五层原型:EventTarget.prototype,这里面最重要的函数属性是addEvementListener(事件捕获和冒泡相关)

最后一层原型:即Object.prototype

节点与元素的区别

节点包括以下几种(更详细的请查询MDN文档):

通过x.nodeType得到一个代表节点类型的数字,

  • 1表示元素Element,也叫标签Tag
  • 3表示文本Test
  • 8表示注释Comment
  • 9表示文档Docu
  • 11表示文档片段DocuFragment

主要需要记忆一和二两条。

节点的增删改查

创建一个标签节点

  1. let div1 = document.createElement('div')
  2. doucument.createElement('style')
  3. doucument.createElement('script')
  4. doucument.createElement('li')

创建一个文本节点

  1. text1 = document.createTestNode('你好')

标签里面插入文本

  1. div1.appendChild(text1)
  2. div1.innerTest = '你好'
  3. div1.textContent = '你好' //不能使用div1.appendChild('你好')

如何将节点插入页面中

创建的标签默认处于JS线程中,需要把其查到head或body里面,才会生效

document.boy.appendChild(div)或者已在页面的元素.appendChild(div)

appendChild

如果同时将一个节点插入两个地方,节点会出现在代码最后插入的位置,一个元素崩同时出现在两个地方,除非复制一份。

两种方法:

旧:parentNode.removeChild(childNode)

新:childNode.remove()

写标准属性

  1. classdiv.calssName = 'red blue' //全覆盖,所有该class都将遭到修改
  2. classdiv.classList.add('red') //追加class'red'
  3. stylediv.style = 'width:100px;color:blue;'
  4. style的一部分:div.style.width = '200px'
  5. 注意大小写:div.style.backgroundColor = 'white'
  6. data-*属性:div.dataset.x = 'frank'

读标准属性

  1. div.classList/a.herf //某些浏览器可能会加工改变最后结果,一般来说两种都可以
  2. div.getAttribute('class')/a.getAttribute('herf')

改事件处理函数

举例说明:

div.onclick默认为null,将div.onclik修改为一个函数fn。

当当点击div时,浏览器就会调用该函数。fn.call(div,evevt)

div当作this,event包含点击事件的所有信息。

改文本内容

  1. div.innerTest = 'xxx'
  2. div.textContent = 'xxx'

两者几乎没有区别

改HTML内容

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

改标签

div.innerHTML = ''    //先清空
div.appendChild(div2)  //再加内容

改父元素

newParent.appendChild(div)

查父元素

node.parentNode或者node.parentElement

查父元素的父元素

node.parentNode.parentNode

查子代

node.childNode或者node.children 优先使用后者

当子代变化时,两者会实时变化,如果不需要变化可以使用document.querySelectorAll()

查同级元素

node.parentNode.childNodes需要排除自己

node.parentNode.children需要排除自己

查第一个子元素

node.firstChild

查看最后一个子元素

node.lastChild

查看上一个同级元素

node.previousSibling

查看下一个同级元素

node.nextSibling

注意:最后两个在查询过程中可能会查到文本节点,可以改为node.previousSibling

遍历元素

travel1 = (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的操作是跨线程的

浏览器中有JS引擎,渲染引擎,分为不同线程。

各线程各司其职,JS引擎只能操作JS而不能操作页面,渲染引擎只能操作页面不能操作JS。

跨线程通信

document.body.appendChild(div1)

当浏览器发现JS在body里面加了一个div1对象,就会通知渲染引擎在页面里也新增一个div元素,新增的div元素所有属性都照抄div1对象。

  • 在div1放入页面之前,所有对div1的操作都属于JS线程内的操作
  • 在div1放入页面之时,浏览器发现JS的意图,通知渲染线程在页面中渲染div1对应的元素
  • 在div1放入页面之后,对div1的操作都有可能会触发重新渲染。

DOM的属性同步

  • 当对div1的标准属性进行修改,会被浏览器同步到页面上,例如id,className,title等
  • data-*属性同标准属性
  • 非标准属性的修改只会停留在JS线程中,不会同步到页面里

Property与Attribute

property属性

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

attribute也是属性

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

区别

大部分时候,同名的property和attribute值相等

但如果不是标准属性,那么它俩只会在一开始时相等

但注意attribute只支持字符串

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