JavaScript DOM编程

芝麻凛 2021年08月26日 124次浏览

JavaScript DOM编程

DOM想传达的一个概念就是:网页其实是一棵树

image20210902194629403.png

JavaScript如何操作这棵树:浏览器往window上加了一个document

JS使用document操作网页,这就是Document Object Model 文档对象模型

获取元素的API

有很多API

  • window.idxxx 或者直接idxxx
  • document.getElementById('idxxx')
  • document.getElementsByTagName('div')[0],获取到的是一个伪数组,[下标]可选择其中一个
  • document.getElementsByClassName(‘red’)[0]
  • document.querySelector('#idxxx')
  • document.querySelectorAll(‘.red’)[0]

用哪一个

  • 工作中用querySelector 和 querySelectorAll
  • 做demo 直接用idxxx,别被人发现
  • 需要兼容IE才使用getElement(s)ByXXX

获取特定元素

获取html元素

  • document.documentElement

获取head元素

  • document.head

获取body元素

  • document.body

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

  • window

获取所有元素

  • document.all
  • 这个document.all 是个奇葩,第六个falsy值

获取到的元素是什么,显然是一个对象,我们需要搞清楚它的原型

元素的六层原型链

image20210902215214054.png

节点和元素的区别

节点Node包括以下几种

  • MDN有完整描述,x.nodeType得到一个数字
  • 1 表示元素Element,也叫标签Tag
  • 3 表示文本 Text
  • 8 表示注释 Comment
  • 9 表示文档 Docment
  • 11 表示文档片段 DocumentFragment
  • 记住1 和 2 即可

节点的增删改查:程序员的宿命就是增删改查

创建一个标签节点

  • let div1 = document.createElement(‘div’)
  • document.createElement(‘style’)
  • document.createElement(‘script’)
  • document.createElement(‘li’)

创建一个文本及诶点

  • text1 = document.createTextNode(‘你好’)

标签里面插入文本

  • div1.appendChild(text1)
  • div.innerText=‘你好’ 或者 div1.textContent = ‘你好’
  • 但是不能用 div1.appendChild(‘你好’)

插入页面中

  • 创建的标签默认处于JS线程中
  • 必须把它插入到head 或者 body里面,它才会生效
  • document.body.appendChild(div) 或者 已在页面中的元素.appendChild(div)

appendChild

代码

页面中有div#test1 和 div#test2
let div = document.createElement('div')
test1.appendChild(div)
test2.appendChild(div)
那么最终div会在test2里面,一个元素不能同时出现在两个地方,除非复制一份
let div2 = div1.cloneNode(true)//true 深拷贝,false浅拷贝
document.head.appendChild(div1)
document.body.appendChild(div2)

删除节点

两种方法

  • 旧方法:parentNode.remove(div)
  • 新方法:div.remove()

思考

如果一个node被移出页面(DOM树),那么它还可以再次回到页面中吗?

document.body.appendChild(div) 添加回来,是可以的,因为只是移出了,还在内存里面。

彻底删除 div = null,等于null了就会被垃圾回收掉。

改属性

写标准属性

  • 改class:div.className = ‘red blue’ (全覆盖)div.className += ‘green’ (正常添加)
  • 改class:div.classList.add('red')
  • 改style:div.style='width:100px;color:blue;'
  • 改style的一部分:div.style.width='200px'
  • 大小写:div.style.bakckgroundColor='white'
  • 改data-*属性:div.dataset.x='test'

读标准属性

  • duv.classList / a.href
  • div.getAttribute(‘class’) / a.getAttribute(‘href’)
  • 两种方法都可以,但是值可能稍微有些不同。

改事件处理函数

div.onclick默认为null

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

div.addEventListener是div.onclick的升级版

改内容

  • 改文本内容
  • div.innerText='xxx'
  • div.textContent='xxx'
  • 两者几乎没有区别

改HTML内容

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

改标签

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

改parent

  • newParent.appendChild(div)
  • 直接这样写就可以,从原来的地方消失

查parent

  • node.parentNode 或者 node.parentElement

查爷爷

  • node.parentNode.parent.Node

查子代

  • node.childNodes 或者 node.children,前者包括文本节点后者不包括文本节点
  • 思考:当子代发生变化是,两者也会实时变化吗? 都会实时变化。

查兄弟姐妹

  • node.parentNode.childNodes 排除自己和所有的文本
  • node.parentNode.children 排除自己
let siblings = []
let c = div.parentElement.children
for(let i=0;i<c.length;i++){
    if(c[i] !== div){
        siblings.push(c[i])
    }
}

查看老大

  • node.firstChild

查看老幺

  • node.lastChild

查看上一个哥哥/姐姐

  • node.previousSibling

查看下一个弟弟/妹妹

  • node.nextSibling

遍历一个div里面的所有元素

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

DOM操作是跨线程的

浏览器分为渲染引擎(渲染HTML和CSS)和JS引擎(执行JS)

跨线程操作

各线程各司其职

  • JS引擎不能操作页面,只能操作JS
  • 渲染引擎不能操作JS,只能操作页面
  • document.body.appendChild(div1) 这句JS是如何改变页面的

跨线程通信

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

图解跨线程操作

image20210902231738518.png

插入新标签的完整过程

在div1放入页面之前

  • 对div1所有的操作都属于JS线程内的操作

把div1放入页面之时

  • 浏览器会发现JS的意图
  • 会通知渲染线程在页面中渲染div1对应的元素

把div1放入页面之后

  • 对div1的操作都有可能会触发重新渲染
  • div1.id = ‘newid’ 可能会重新渲染,也可能不会,因为可能存在CSS样式
  • div.title='new' 可能会重新渲染,也可能不会,
  • 如果连续对div1多次操作,浏览器可能会合并成依次操作,也可能不会

属性同步

标准属性

  • 对div1的标准属性的修改,会被浏览器同步到页面中
  • 比如id、className、title等

*data-属性

  • 同上

非标准属性

  • 对非标准属性的修改,则只会停留在JS线程中
  • 不会同步到页面里,比如自定义属性

启示

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

image20210902233459333.png

Property v.s. Attribute

property属性

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

attribute也是属性

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

区别

  • 大部分时候,同名的property 和 attribute 值相等
  • 但如果不是标准属性,那么它俩只会在一开始相等
  • 但注意attribute只支持字符串,而property支持字符串、布尔等类型