DOM操作

158 阅读4分钟

DOM

Document Object Model 文档对象模型

获取元素,元素也叫标签

<div class='acitve' id='app'></div>
//
window.app
app //若使用的为保留字,或已存在属性,则无法获取
document.getElementById('app')
document.getElementsByTagName('div')[0]
document.getElementsByClassName('acitve')[0]
document.querySelector('#app') //使用css语法
document.querySelectorAll('.active')[0]

获取特定元素

//获取html元素
document.documentElement

//获取head元素
document.head

//获取body元素
document.body

//获取窗口(窗口不是元素)
window

//获取所有元素
document.all
!!document.all //false
//document.all为第六个falsy值,

获取到的元素都是对象

  • console.dir(document.querySelectorAll('div')[0])可查看原型
  • 自身属性,className,id,style等等
  • 第一层原型HTMLDivElement.prototype,所有div共有的属性
  • 第二层原型HTMLElement.prototype,所有HTML标签共有的属性
  • 第三层原型Element.prototype,所有XML、HTML标签的共有属性
  • 第四层原型Node.prototype,所有节点共有的属性,节点包含XML标签文本注释、HTML标签文本注释等
  • 第五层原型EventTarget.prototype,最重要的函数属性为addEventLisener
  • 最后一层为Object.prototype

节点和元素的区别,节点Node包括了以下几种

  • x.nodeType得到一个数字
  • 1表示元素Element,也叫标签Tag
  • 3表示文本Text
  • 8表示注释Comment
  • 9表示文档Document

节点的增删改查

//创建一个标签节点
let div1 = document.createElement('div')
document.createElement('script')
document.createElement('style')

//创建一个文本节点
let text = document.createTextNode('jack')

//在标签里插入文本
div1.appendChild(text) //追加
div1.innerHTML = 'hi' //替换
div1.textContent = 'hi' //替换

//创建的标签默认在js线程中
//必须插入head或body才会生效
//document.body.appendChild(div1)
//或者[已在页面中的元素].appendChild(div1)

//insertBefore函数
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
//insertedNode 返回值为被插入节点(相当于newNode)
//parentNode 新插入节点的父节点
//newNode 用于插入的节点
//referenceNode newNode 将要插在这个节点之前
<div id='divFater'>
    <div id='big'></div>
</div>

let o = document.createElement('div')
divFater.insertBefore(o,big)

apendChild

<div id="test1"></div>
<div id="test2"></div>
//页面中有两个div

let div1 = document.createElement('div')
div1.textContent = 'hahahaha'
test1.appendChild(div1)
test2.appendChild(div2)
//最终只会存在一个,出现在后面被添加的元素test2中
//一个元素不能出现在两个地方,除非赋值一份
div1.cloneNoed(true) //深拷贝

//两种方式
//第一种,找到爸爸再删除自己
div1.parentNode.removeChild(div1)
//只是被移出dom树,还存在js内存中
div1 = null //没有被引用的对象会被js的垃圾回收机制回收
test2.appendChild(div1) //再次回到页面中
div.remove()

写标准属性
div1.id = 'test' //id改为'test',没有则增加
div1.className = 'red blue' //改class覆盖原有属性,多个class用空格隔开
div1.classList.add('blue') //改class在原有属性上新增,不覆盖

div1.style = 'width:200px;color:red;' //替换整个style
div1.style.color = 'yellow' //修改部分
div1.style.background-color //js会当成 div1.style.background减去color
div1.style.backgroundColor = 'green' //去掉-符号,改为大写即可

div1.setAttribute('data-x','test')
div1.dataset.x = '222' 
//可以通过修改data-*获取属性

读标准属性

div.id
div.style
div.className
div.href //如果是相对路径,获取的路径会加上协议域名等如("http://localhost:1234/sss")
div.getAttribute('href') //(/sss)

改事件处理函数

  • div.onclick === null //ture,默认为null
  • 默认点击div不会有任何事情发生
  • 如果把onclick赋值为一个函数
  • 点击div时浏览器就会调用这个函数
  • 并且是这样调用的fn.call(div,event)
  • div会被当做this
  • event则包含了点击事件的所有信息,如坐标
  • div.addEventListener //

改内容

  • div.innerText = 'xx'
  • div.textContent = 'xx'
  • 两者几乎没差别
  • div.innerHTML="<strong>i</strong> //改HTML内容
  • 改标签
div.innnerHTML = ''
div.appendChild(newChild)
  • 改爸爸newParent.appendChild('div')因为元素只能存在一个地方,原来的位置会消失

//查爸爸
div.parentNode
div.parentElement

//查爷爷
div.parentNode.parentNode
div.parentElement.parentElement

//查儿子
node.childNodes //所有node儿子
node.children //所有element儿子
//这俩api都会实时变化,querySelector则不会

//查兄弟姐妹,先获得爸爸的所有儿子,再排除自己
let allSiblings = div1.parentNode.children //使用parentNode会更麻烦
let notMeList = []

for(let i=0;i<allSiblings.length;i++){
    if(allSiblings[i]!==div1){
        notMeList.push(allSiblings[i])
    }
}

//查看第一个儿子
div1.parentElement.children[0]
div1.firstChild

//查看最后一个儿子
div1.parentElement.children[div1.parentElement.children.length-1]
div1.lastChild

//查看上一个兄弟姐妹
div1.previousSibling //包含文本节点
div1.previousElementSibling //只包含元素节点

//查看下一个兄弟姐妹
div1.nextSibling //包含文本节点
div1.nextElementSibling //只包含元素节点

DOM操作是跨线程的

跨线程操作
  • 各线程各司其职
  • JS引擎不能操作页面,只能操作JS
  • 渲染引擎不能操作JS,只能操作页面
跨线程通信
  • document.body.appendChild(div1)
  • 当浏览器发现JS在body里面加了div1对象
  • 浏览器会通知渲染引擎在页面里新增一个div元素
  • 新增的div元素所有属性都照抄div1

渲染合并

//html
<div id='app'></div>
//css
.star{
    border:1px red solid;
    width:100px;
    height:100px;
    transition:width 1s;
}

.end{
    width:200px;
}

//js
//浏览器会合并为一次操作,不会有动画效果
app.classList.add('star')
app.classList.add('end') 

//加一个获取属性,因为要获取所以浏览器必须先渲染,触发重新渲染
app.classList.add('star')
app.clientWidth
app.classList.add('end') 

属性同步

  • 标准属性 id,className,title等会自动同步
  • data-*属性同上
  • 非标准属性,如自己设置的jkj='111',只会停留在js线程内,不会同步到页面中
<div id='a' data-j='b' jk='c'></div>

let div = document.querySelector('#a')
div.id = 'qwq' //会同步
div.dataset.j = 'asdsa' //会同步
div.jk = 'rtrt' //不会同步

Property vs Attribute

  • js线程中div的所有属性叫做div的property
  • 渲染引擎中div对应标签的属性,叫做attribute

区别

  • 大部分时候,同名的propery和attribute的值相等
  • 如果不是标准属性只在一开始相等,后续更改无效
  • attribute只支持字符串