31. DOM编程
最后更新于
这有帮助吗?
最后更新于
这有帮助吗?
网页其实是一棵树。
JS如何操作这棵树?
浏览器往window上加一个document即可。
或者也可以通过id来获取元素,比如网页上(baidu.com)某个元素的id是readIcon
,可以通过下面两种方式获取元素。
JS用document操作网页,这就是Document Object Model文档对象模型。
记住一个事实,DOM很难用。
如果你觉得DOM很傻,不要怀疑自己,你觉得的是对的。
DOM自带的功能非常反人类,所以一开始用jQuery来操作DOM,再后来用Vue和React来操控DOM。
除了window.idxxx
或者直接idxxx之外,还有其他方法来获取元素。比如:
那么问题来了,当你知道可以通过window.idxxx和idxxx就可以直接获取元素,还会使用上面繁琐冗长的写法吗?
会,某些情况下会,当你的id和全局属性冲突的时候下还是需要用到上面获取元素的方法的,比如window.parent
工作中用querySector
和querySectorAll
例子:
要兼容IE的可怜虫才用getElementsByXXX。
做demo直接用idxxx,千万别让人发现。
获取html元素
document.documentElement
获取head元素
document.head
获取body元素
document.body
获取窗口(窗口不是元素)
window
有什么用呢,可以用来监听事件
此时点击页面,就能看到console出hi
获取所有元素
document.all
这个document.all是个奇葩,第6个falsy值
上面这段代码打印出来的是4,为什么呢?
因为document.all是IE发明出来的,早期的程序员通过document.all存不存在来区分是否是IE浏览器。
获取到的元素是个啥?
显然是一个对象,我们需要搞清它的原型。
console.dir
可以打印出元素的结构/看原型链
告诉你一个秘密,Chrome 显示错了
自身属性:className、id、style 等等
第一层原型 HTMLDivElement.prototype
这里面是所有 div 共有的属性,不用细看
第二层原型 HTMLElement.prototype
这里面是所有 HTML标签共有的属性,不用细看
第三层原型 Element.prototype
这里面是所有 XML、HTML 标签的共有属性,你不会以为浏览器
只能展示 HTML吧
第四层原型 Node.prototype
这里面是所有节点共有的属性,节点包括 XML 标签文本注释、
HTML 标签文本注释等等
第五层原型 EventTarget.prototype
里面最重要的函数属性是 addEventListener
最后一层原型就是 Object.prototype了
例子:在Element.prototype里有个requestFullscreen()
可以用来请求全屏
Node和Element的区别
节点Node包括以下几种:
MDN 有完整描述,x.nodeType 得到一个数字
1表示元素 Element, 也叫标签 Tag
3表示文本 Text
8表示注释 Comment
9表示文档 Document
11 表示文档片段 DocumentFragment
记住1和2即可
例子:
程序员的宿命就是增删改查
增
创建一个标签节点
创建一个文本节点
标签里面插入文本,你好是一个字符串,text1是一个对象
但是不能用 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 出现在哪里? 1.test1 里面 2.test2 里面 3.test1 里面和test2 里面
答案是2(送子观音)
一个元素不能出现在两个地方,除非复制一份
如何深拷贝node
两种方法
旧方法:parentNode.childChild(childNode)
新方法:childNode.remove()
思考
如果一个node被移除页面(DOM树)
那么它还可以再次回到页面中吗?
写标准属性
改class:div.className = 'red blue'
(全覆盖)
改class:div.classList.add('red')
改style:div.style = 'width:100px; color:blue;'
改style的一部分: div.style.width = '200px'
大小写: div.style.backgroundColor = 'white'
改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的升级版
改文本内容
两者几乎没有区别
改HTML内容
改标签
改爸爸
想要找一个新爸爸?
newParent.appendChild(div)
直接这样就可以了,直接从原来的地方消失
查爸爸
node.parentNode或者node.parentElement
查爷爷
node.parentNode.parentNode
查子代
node.childNodes或者node.children
思考:当子代变化时,两者也会实时变化吗?
查兄弟姐妹
node.parentNode.childNodes还要排除自己
node.parentNode.children还有排除自己
查看老大
node.firstChild
查看老幺
node.lastChild
查看上一个哥哥/姐姐
node.previousSibling
查看下一个弟弟/妹妹
node.nextSibling
遍历一个div里面的所有元素
看数据结构多么有用
DOM操作是跨线程的
还记得《JS世界》里讲的浏览器功能划分吗?浏览器分为渲染引擎和JS引擎。
各线程各司其职
JS引擎不能操作页面,只能操作JS
渲染引擎不能操作JS,只能操作页面
document.body.appendChild(div1)
JS是如何改变页面的?
跨线程通信
当浏览器发现JS在body里面加了个div1对象
浏览器就会通知渲染引擎在页面里也新增一个div元素
新增的div元素所有属性都照抄div1对象
图示跨线程操作
在div1放入页面之前
你对div1所有的操作都属于JS线程内的操作
把div1放入页面之时
浏览器会发现JS的意图
就会通知渲染线程在页面中渲染div1对应的元素
把div1放入页面之后
你对div1的操作都有可能会触发重新渲染
div1.id = 'newId' 可能会重新渲染,也可能不会
div1.title = 'new', 可能会重新渲染,也可能不会
如果你连续对div1多次操作,浏览器可能会合并成一次操作,也可能不会
标准属性
对div1的标准属性的修改,会被浏览器同步到页面中
比如id,className,title等
*data-属性
同上
非标准属性
对非标准属性的修改,则只会停留在JS线程中
不会同步到页面里
比如x属性,示例代码
启示
如果你有自定义属性,又想被同步到页面中,请使用data-作为前缀
图示
property属性
JS线程中div1的所有属性,叫做div1的property
attribute也是属性
渲染引擎中div1对应标签的属性,叫做attribute
区别
大部分时候,同名的property和attribute值相等
但如果不是标准属性,那么它俩只会在一开始时相等
但注意attribute只支持字符串
而property支持字符串,布尔登类型