网页是一棵树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支持字符串或者布尔值、数字等类型