JS中DOM编程总结
Get Started
- 网页结构
- 节点的增删改查
- 跨线程操作
- 属性同步
- Property 与 Attribute 的区别
网页其实是一颗树。
JS如何操作这个树,浏览器往window上加一个document即可。
JS用document操作网页,这就是Document Object Model文档对象模型。
事实:DOM很难用
获取元素,也叫标签(节点包括元素和文本等)
• 有很多API
window.idxxx或者直接idxxx
document.getElementByld('idxxx')
document.getElementsByTagName('div')[0]
document.getElementsByClassName('red')[0]
document.querySelector('#idxxx')
document.querySelectorAll('.red')[0]
• 有s的都得加下标
• 用哪个
工作中用querySelector和querySelectorAll
要兼容IE的才用getElement(s)ByXXX
获取特定元素
• 获取html元素
document.documentElement
• 注意:相当于属性,而不是函数,不用加()
• 获取head元素
document.head
• 获取body元素
document.body
• 获取窗口(窗口不是元素)虽然window不是标签,但是有时候会获取window加一些全局的事件监听
window
• 获取所有元素(将内部的所有标签一字排开)
document.all
这个是个奇葩,第6个falsy值,使用if之后为假。
我们获取到的元素是一个对象,抓取一只div对象看一下
console.dir(div1)
看原型链
• ※Chrome显示错了(显示内容.pritotype)
• 自身属性:className、id、style等
• 第一层原型HTMLDivElement.prototype
• 这里面是所有div共有的属性,不用细看
• 第二层原型HTMLElement.prototype
• 这里面是所有HTML标签共有的属性,不用细看
• 第三层原型Element.prototype
• 这里面是多有XML、HTML标签的共有属性。
• 第四层原型Node.prototype
• 这里是所有节点共有的属性,节点包括XML标签文本注释、HTML标签文本注释等等
• 第五层原型EventTarget.prototype
• 里面最重要的函数属性是addEventListener
• 最后一层原型就是Object.prototype了
节点?元素?
节点Node包括一下几种
• MDN有完整描述,x.nodeType得到一个数字
• 1 表示元素Element,也叫标签Tag
• 3 表示文本Text
• 8 表示注释Comment
• 9 表示文档Document
• 11 表示文档片段DocumentFragment
• 记住1和3即可
注意:回车也算一个文本节点。
节点的增删改查
增
• 创建一个标签节点
let div1 = document.createElement('div')
document.createElement('style')
document.createElement('script')
document.createElement('li')
• 创建一个文本节点
text1= =document.createTextNode('你好')
• 标签里面插入文本
div1.appendChild(text1)
(这个是Node提供的接口)
'div1.innerText = '你好'(IE) 或者 div.textContent = '你好''(标准)(这里是element提供的接口)
但是不能用div1.appendChild('你好')
• 插入页面中
创建的标签默认处于JS线程中
你必须把他查到head或者body里面,它才会生效
document.body.appendChild(div)
或者
已在页面中的元素.appendChild(div)
append
• 代码
页面中有div#test1和div#test2
let div = document.createElement('div')
test1.appendChild(div)
test2.appendChild(div)
• 请问最终div出现在哪里?
答:test2里面
一个元素不能出现在两个地方,除非复制一份。
用cloneNode(let div1 = div.cloneNode())
删
• 两种方法
旧方法:parentNode.removeChild(childNode)
div.parentNode.removeChild(div)
新方法:childNode.remove()(ie不支持)
div.remove()
• 思考
如果一个node被移出页面(DOM树)
那么它还可以再次回到页面中吗?
答:是可以的,只是移回了内存,还是存在的,将null赋值给div让其与内存断开连接,就会被内存回收掉
改
• 写标准属性
改class:div.className = 'blue'
(全覆盖、class优先判定为关键字)(+=' red'一般不用)
改class:div.classList.add('red')
改style:div.style='width: 100px;color: blue';
改style的一部分:div.style.width='200px'
大小写:div.style.backgroundColor='frank'
改data-*属性:div.dataset.x = 'frank'
• 读标准属性
div.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)
再加内容改爸爸
• 想找一个新爸爸
newParent.appendChild(div)
直接这样就可以了,直接从原来的地方消失
查
• 查爸爸
node.parentNode或者node.parentElement
• 查爷爷
node.parentNode.parentNode
• 查子代
node.childNodes或者node.children
(childNodes如果有空格的话会被当成文本占用长度)(都会实时更新)
注意:若用document.querySelectorAll('li')来获取元素,那么获取的元素长度不变
• 查兄弟姐妹
node.parentNode.childNodes
还要排除自己和所有的文本节点
node.parentNode.children
还要排除自己
• 查看老大
node.firstChild
• 查看老幺
node.lastChild
• 查看上一个哥哥/姐姐
node.previousSibling
(这里面会有文本节点,node.previousElementSibling没有)
• 查看下一个弟弟/妹妹
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)
}
}
}
travel(div1, (node) => console.log(node))
跨线程操作
- 各线程各司其职
JS引擎不能操作页面,只能操作JS。渲染引擎不能操作JS,只能操作页面。 - 跨线程通信
当浏览器发现JS在body里面加了个div1对象,浏览器就会通知渲染引擎在页面里也新增一个div元素,新增的div元素所有属性都照抄div1对象。
在浏览器发现需要渲染引擎去渲染新的元素时,浏览器会通知渲染引擎,这个过程是需要消耗时间的。
插入一个新的标签的完整过程
- 在div1放入页面之前,对div1所有的操作都属于JS线程内的操作
- 把div1放入页面之时,就会通知渲染线程在页面中渲染div1对应的元素
- 把div1放入页面之后,对div1的操作都有可能会触发重新渲染,div1.id = 'newld'可能会重新渲染,也可能不会,div1.title = 'new'可能会重新渲染,也可能不会,如果你连续对div1多次操作,浏览器可能会合并成一次操作,也可能不会(之前在动画里提到过)
属性同步
- 标准属性
对div1的标准属性的修改,会被浏览器同步到页面中,比如id、class Name、title等 - data-*属性
同上 -
非标准属性
对非标准属性的修改,则只会停留在JS线程中,不会同步到页面里。
启发:如果你有自定义属性,又想被同步到页面中,请使用data- 作为前缀作为属性名。
非标准属性.png
示意图.png
Property 与 Attribute 的区别
- property属性
JS线程中div1 对象的所有属性,叫做div1的 property - attribute也是属性
渲染引擎中div1对应标签的属性,叫做attribute,或者说页面标签中的属性 - 区别
大部分时候,同名的property和attribute值相等,但如果不是标准属性,那么它俩只会在一开始时相等。但注意attribute只支持字符串,而property支持字符串、布尔等类型。