你可以看到
如何获取元素
节点的增删改查
DOM跨线程操作
要说的话
到这里总算不是门外汉了,这才是程序员的开始。
--资料来源于饥人谷
DOM编程
JS用document操作网页,这就是Document Object Model文档对象模型。
什么是DOM呢,就是把网页抽象成一个document对象,并对它进行操作的一种方式就是DOM
- 网页其实是一棵树
看一下代码结构
看树每个节点
JS 如何操作这棵树
浏览器往window上加一个document即可
- JS 用document 操作网页
这就是Document Object Mod,DOM
- DOM很难用
API很长,不好用,不得不来封装
d
获取元素,也叫做标签
获取任意元素的API
1. window.idxxx或者直接idxxx
2. document.getElementByld('idxxx')如果id和全局变量冲突了可以用这个
3. document.getElementsByTagName('div')[0]找到所有标签名为div的元素,只有加下标才可以来操作对应div。 比如
4. document.getElementsByClassName('red')[0]根据class类名来获取,比如把所以类名class为red的标签用下标获取到,取第0个下标即第1个元素。
5. document.querySelector('#idxxx')
可以像css选择器一样写的很复杂,比如某个div里的span并且还是div的第二个儿子
document.querySelectorAll('.red')[0]
找到所有满足这个条件的元素
- 用哪一个
工作中用querySelector
和 querySelectorAll
,要兼容IE的才用getElement(s)ByXXX
获取特定元素
- 获取html根元素
document.documentElement 好长啊
如果你打出它的标签名的话:document.documentElement.tagName获取html元素的标签名
- 获取head元素
document.head
- 获取body元素
document.body
- 获取窗口(窗口不是元素)
window
window虽然不是一个标签,但我们有时确实会获取到这个window,然后可以添加一些事件监听
- 获取所有元素
document.all
可以看到当前页面一共有多少个标签。
获取到的元素是个啥
获取到的所有元素显然是一个对象,我们需要搞清它自身有哪些属性以及它的原型。
抓一只div对象来看看let div = document.getElementsByTagName('div')[9]
。然后用console.dir(div1)
看原型链。可以看到六层原型链
元素的六层原型链
先上图
- 第一层原型HTMLDivElement.prototype
这里面是所有div共有的属性,不用细看。
- 第二层原型HTMLElement.prototype
这里面是所有HTML标签共有的属性,不用细看
- 第三层原型Element.prototype
这里面是所有XML、HTML标签的共有属性,你不会以为浏览器只能展示HTML吧
- 第四层原型Node.prototype
这里面是所有节点共有的属性,节点包括XML标签文本注释、HTML标签文本注释等等
- 第五层原型EventTarget.prototype
里面最重要的函数属性是addEventListener
- 最后一层原型就是Object.prototype 了
节点?元素?傻傻分不清
在任意一个节点上输入.nodeType可以获取到它的node类型 节点包括元素和文本等,元素就是标签,叫法不同而已。
- 节点Node包括以下几种
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')
好长啊,所以要去使用jQuery,vue,react
- 创建一个文本节点
text1 = document.createTextNode('你好')
- 标签里面插入文本
div1.appendChild(text1) --Node提供的接口
div1.innerText= '你好'或者div1.textContent= '你好' --Element提供的接口
但是不能用div1.appendChild('你好')
- 插入页面中
你创建的标签默认处于JS线程中,你必须把它插到head或者body里面,它才会生效
用document.body.appendChild(div),或者,已在页面中的元素.appendChild(div),才可以把div放入对应位置
appendChild
- 代码
假如页面中有 div#test1 和 div#test2
let div = document.createElement( 'div ' )
test1.appendChild(div)
test2.appendChild(div)
-
请问最终div 出现在哪里?
-
test1里面
-
test2里面
-
test1里面和test2里面
-
删
- 两种方法
旧方法:parentNode.childChild(childNode) 找到粑粑搧掉儿子
新方法: childNode.remove() 不兼容IE。一样的效果
- 思考
如果一个node被移出页面(DOM树)
那么它还可以再次回到页面中吗?
还可以,只是从树移到了内存里
彻底删掉要让它为空,内存就会把它当垃圾回收掉了。
改属性
- 写标准属性
改id:div1.id = "div1"
改全class: div.className = 'red blue'(全覆盖,每次改会把之前的覆盖了)
改一部分className:div.className += ' red'(在原来的基础上加上空格red。)
改class: div.classList.add('red') 新的API, classList可以告诉你class有哪些,长度多少。
改style: div.style = 'width: 100px; color: blue;'(原先的无了,被覆盖了)
改style的一部分:div.style.width = '200px'
大小写:div.style.backgroundColor = 'white'就是把-后面接的字母变成大写。或者div.style['background-color']。
改data-*属性: div.dataset.x = 'dw'(div.dataset。在属性名前加上data-,通过dataset.属性名就可以得到内容。改的话直接 = '要改的') 自定义属性。
- 读标准属性
基本上名字上一一对应的
div.classList / a.href 这种可能会有问题,例子
div.getAttribute('class') / a.getAttribute('href')获取原原本本的值
两种方法都可以,但值可能稍微有些不同
改事件处理函数
还可以改on开头的属性
- div.onclick默认为null
代码示例
默认点击div不会有任何事情发生,但是如果你把div.onclick改为一个函数fn,那么点击div的时候,浏览器发现这个div的onclick是有的,于是就会调用这个onclick函数,并且浏览器是用call调用的fn.call(div, event)(触发元素的引用,事件的详细信息)。fn.call(div, event)里div会被当做this,第一个参数就是event则包含了点击事件的所有信息,如坐标。函数是被调的,这样就可以传个参数。
- div.addEventListener
是div.onclick的升级版.onclick只能写一个函数,写第二个会覆盖前面的。addEventListener可以写无数个函数。
改内容
改儿子
- 改文本内容
div.innerText = 'xxx' IE写的
div.textContent = 'xxx' 标准浏览器
两者几乎没有区别,浏览器同时支持这两个
- 改HTML内容
div.innerHTML = '<strong>重要内容</strong>'内容太多太多字符(2W字符左右)可能会卡浏览器
- 改标签
div.innerHTML= "//先清空
div.appendChild(div2)/再加内容
改爸爸
- 想要找一个新爸爸?
newParent.appendChild(div)在这个新爸爸节点调用appendChild把自己放进去
直接这样就可以了,直接从原来的地方消失,到新爸爸家里去了
查
- 查爸爸
node.parentNode或者node.parentElement
- 查爷爷
node.parentNode.parentNode 爸爸的爸爸叫爷爷以此类推
- 查子代
node.childNodes(包括文本节点)或者node.children(不包括文本节点)
console.log(node.childNodes.length)打印出子节点的长度。可能会获取到你不想要的,空格也算一个长度(包括文本节点)。 代码示例
一般用node.children
(不包括文本节点)
- 思考:
当子代变化时,两者也会实时变化吗?
都会实时更新,但querySelectorAll不会实时更新
- 查兄弟姐妹
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))
DOM是跨线程的
浏览器分为渲染引擎和JS引擎,这两个在不同线程,互不相干
跨线程操作
- 各线程各司其职
JS引擎不能操作页面,只能操作JS
渲染引擎不能操作JS,只能操作页面
document.body.appendChild(div1)
这是JS调用,理论只能存在内存里,为什么可以出现在屏幕上呢,这句JS是如何改变页面的?
- 跨线程通信
当浏览器发现JS在body里面加了个div1对象
浏览器就会通知渲染引擎在页面里也新增一个div元素
新增的div元素所有属性都照抄div1对象
图示跨线程操作
时间花在浏览器发现并通知上,显得更新div的操作会比其他的所有操作都要慢
插入新标签的完整过程
- 在div1放入页面之前
你对div1所有的操作都属于JS线程内的操作
- 把div1放入页面之时
浏览器会发现JS的意图
就会通知渲染线程在页面中渲染div1对应的元素
- 把div1放入页面之后
你对div1的操作都有可能会触发重新渲染
div1.id = 'newld'可能会重新渲染,也可能不会
div1.title = 'new'可能会重新渲染,也可能不会。可以看CSS tricks
如果你连续对div1多次操作,浏览器可能会合并成一次操作,也可能不会(以前动画里提到过,代码示例)
属性同步
- 标准属性
对div1的标准属性的修改,会被浏览器同步到页面中。比如id、className、title等
- data-*属性
同上
- 非标准属性
对非标准属性而且也不是data的修改,则只会停留在JS线程中 ,不会同步到页面里
比如x属性,示例代码
- 启示
如果你有自定义属性,又想被同步到页面中,请使用data-作为前缀
图示
Property v.s.Attribute
- property属性
指的是JS线程中div1的所有属性(也就是div的在内存里的那个对象),叫做div1的property(比如id是它的property,className也是它的property)
- attribute也是属性
指的是渲染引擎中div1对应 标签的属性(比如id='',那id=''就是它的属性),叫做attribute
- 区别
大部分时候,同名的property和attribute值相等
但如果不是标准属性,那么它俩只会在一开始时相等
但注意attribute只支持字符串
而property支持字符串、布尔等类型
DOM操作慢么?
网上都说 DOM 操作慢,实际上只是比 JS 操作慢,DOM 操作比网络请求还是快很多的。
关于这一部分内容,大家可以延伸阅读参考一些文章:
-
为什么说DOM操作很慢
-
为什么经过10年的努力DOM操作还是这么慢
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!