DOM编程

684 阅读5分钟

网页是一棵树tree

回顾上一篇文章数据结构(一),我们知道数据结构基础,链表、队列、数等数据结构。而我们写得html网页其实是一种树结构。

文档对象模型

浏览器将所有html的标签都放入到window.document这个对象中,利用这个对象,可以对所有的标签进行操作,一般来说就是增删改查。

js利用document来操作网页,称为document Object model 这就是文档对象模型的由来。

各种API

获取元素对象

用id获取对象:有两种方法可以获取对应id名的html标签,大部分人都使用document.getElementById('xx(id名)')来获取,还可以使用window.xx(id名)或者直接使用xxx(id名)。

用class获取对象:document.getElementsByClassName('xxx(class名)')

用标签获取对象:document.getElementsByTagName('div')

上面四种方法都不要用,因为是需要兼容ie才做的选择。一般直接使用document.querySelector('')或者document.querySelectorAll('')来获取

(``)中可以直接写css的选择器,例如(`.parent>div:nth-child(2)`)
('.className')或者('#idName')

如果使用querySelectorAll的话,获取到的所有对象会形成伪数组,请加上索引,例如 document.querySelector('div')[0]获取第1个div。

获取特定元素

获取html:document.documentElement

获取所有元素:document.all

获取head元素:document.head

获取body元素:document.body

注意:document.all是第八个falsy值。

这里延展一个小故事,因为document.all是出自ie浏览器,以前的程序员为了知道用户是不是用的ie,会写这样一段代码

if(document.all){
	这里写的代码兼容ie}
    else{
		这里写的代码是为其他浏览器所写}

后来所有浏览器都沿用了document.all这个API,但是用户的代码没有变。按照逻辑,那么所有程序都会运行兼容ie的代码,这是有悖于浏览器厂商的意愿的,最终大家都达成共识,在浏览器中,document.all就代表着falsy值。

获取到的元素包括了啥

我们获取到的元素是一个对象,是对象都有原型,那么我们来看一下它的原型。

使用console.dir(logo)打印一个对象。这个logo就是网页的id名,一般网页都有id 既然logo是一个对象,那么它的原型是怎样的呢?

一张图表示:

我们可以得出一个元素对象的原型链,一共分为六层,每一层都为元素对象提供原型方法。

元素和节点

元素实际上就是节点里面的子节点,节点包括元素、文本、注释等,通过node.nodeType能获得节点类型,返回的数字是通过以下图来分辨类型的

节点的增删改查

创建一个标签节点

  • let div=document.createElement('div')
  • document.createElement('style')
  • document.createElement('script')
  • document.createElement('li')
  • div.innerHTML='你好'

创建一个文本节点

  • let text=document.createTextNode('你好')

在标签内插入文本

  • div.appendChild(text) 这种方法太烦了,一般不用
  • div.innerText='你好'
  • div.textContent='你好'

因为js线程跟css线程是不同的,在js中更改的内容必须插入到页面中才能生效,所以我们需要在指定位置插入创建的元素,例如: document.body.appenChild(div)

使用appendChild插入元素时,如果将一个元素同时插入两个节点,那么会以最后一次为准。

有两种方式可以删除节点

1、parentNode.removeChild(childNode)

2、childNode.remove()

ps:第一种方法过时了

修改class

div.className = 'red' // 这里的修改会覆盖原来的className

div.classList.add('red')

修改style

div.style='width:200px' //这里会覆盖原来的style

div.style.backgroundColor ='red' //修改style的指定部分,注意大小写,DOM编程基本遵循css的样式,但是background-color的-不会被使用,而是用大小写的写法

修改自定义data-属性

div.dataset.x='qiu'

这里附带说一句,使用js填写自定义属性使用.setAttribute('data-xxx','属性值')

读取属性可以使用.getAttribute('data-xxx')

改事件处理函数

元素有很多执行方法,例如事件处理函数,如果不设置的话,它是这样的

默认事件处理方法的api为null。以onclick为例,假设我要给div设置一个点击事项,当点击div后打印1,那么可以给onclick设置函数

div.onclick=function(){
	console.log('1')}

当点击后,浏览器会帮助我们调用这一个callback回调函数,用的方法是fn.call(div,event),div代表要调用的对象,event包含了点击事件中的所有信息,例如坐标x。

改内容

div.innerHTML='<strong>我是加粗体</strong>'

使用innderHTML的方式也可以用来新增元素节点,上面的代码会让div新增一个<strong>我是加粗体</strong>的元素节点

div.innetText='修改后的文本'

div.textContent='修改后的文本'

修改父节点

直接将元素append到新的父节点下就可以 newParent.appendChild(div)

查找父元素节点

div.parentElement

查父节点

div.parentNode

查子元素

div.children

查子节点

div.childNodes

查上一个兄弟节点

div.previousSibling

查下一个兄弟节点

div.nextSibling

查第一个节点

div.firstChild

查最后一个节点

div.lastChild

遍历所有子节点

const travel=(node,fn)=>{
	fn(node)
    if(node.children){
        for(let i=0;i<node.children.length;i++){
    	fn(node.children[i],fn)
    	}
    }
}

线程通信

因为js跟css是跨线程的,所以当js创建一个元素后,想要将内容插入到页面中,需要一种机制通知渲染引擎来重新渲染页面 整个过程是这样的:

  • js先写代码,例如createElement,这里的操作不影响css
  • push节点到css中,这时候浏览器通知css线程修改页面加以渲染
  • 很有可能会重新渲染,也有可能不会

当多次操作时,很有可能会被合成一种操作。例如:

js影响css的渲染机制: qiuyanxi.com/cssxuan-ran…

属性同步

js中,只有修改标准属性会被浏览器同步到css渲染引擎中,例如:class、data-xxx就是标准属性

Property和Attribute

在js线程中的所有属性,为Property

在css线程中的属性,为Attribute

大部分时间这两者相等,但是Attribute肯定是字符串。而property支持字符串或者布尔值、数字等类型