一、网页其实是一棵树
二、JS用document操作网页
html内容由渲染引擎来渲染到页面上,而JS引擎负责的是JS内容,是操作不了文档内容的,咋办? 于是让浏览器来提供办法。 浏览器往window上加一个document对象,这时JS就可以用document来操作网页了,这就是document object model,即文档对象模型(把网页抽象成对象,然后来操作它)
三、获取元素,也叫标签
获取任意元素
1.偷偷用的最简便的方法
window.id
id
- 但是如果id为保留字,比如parent,就不能用了,只能用下面的
2.兼容IE才会用的方法
document.getElementByld('id')
document.getElementsByTagName('标签名')[0]
(因为得到的是所有的这个标签,所以是个数组,你要自己选择需要该数组中的第几个)document.getElementsByClassName('class名')[0]
(同上)
3.工作中的用的方法
doucment.qureySelector('#idxxx')
80%情况下css选择器咋写就咋写doucment.qureySelectorALL('.red')[0]
获取特定元素
- 获取html元素:
document.documentElement
- 获取head元素:
document.head
- 获取body元素:
document.body
- 获取窗口(窗口不是元素):
window
- 获取所有元素:
document.all
( 它是第六个false值)
四、元素是个对象--看看一个div对象的原型链
console.dir(div1)看原型链
- 自身属性:
className
、id
、style
等等 - 第一层原型
HTMLDivElement.prototype
: 这里面是所有div
共有的属性,不用细看 - 第二层原型
HTMLElement.prototype
: 这里面是所有HTML
标签共有的属性,不用细看 - 第三层原型
Element.prototype
: 这里面是所有XML
、HTML
标签的共有属性,你不会以为浏览器 只能展示HTML
吧 - 第四层原型
Node.prototype
: 这里面是所有节点共有的属性,节点包括XML
标签文本注释、HTML
标签文本注释等等 - 第五层原型
EventTarget.prototype
: 里面最重要的函数属性是addEventListener
- 最后一层原型就是
Object.prototype
了
五、节点的增删改查
增
1.创建一个标签节点
document.createElement('标签名')
let div1 = document.createElement('div')
- 你创建的标签默认处于JS线程中,内存中
2.创建一个文本节点
text1 = document.createTextNode("你好")
3.标签里面插入文本
div1. appendChild(text1)
必须加文本节点的名字,不可以加文本的内容div1.innerText='你好'
(Node原型提供)div1.textContent ='你好'
(Element原型提供)
4.插入页面中
- 你创建的标签默认处于JS线程中
- 你必须把它插到
head
或者body
里面,它才会生效 document.body.appendChild(div1)
- 或者
已在页面中的元素.appendChild(div1)
5.一个元素不能同时出现在两个地方
页面中有div#test1和 div#test2
let di = document.createElement('div')
test1.appendChild(div)
test2.appendChild(div)
问:test最终会出现在哪里?
- 出现在div1里面
- 出现在div2里面
- 同时出现在div1和div2里面
答:div2里面,因为一个元素不能同时出现在两个地方,除非复制一份
删
旧方法
parentNode.childChild(childNode)
(找到它的父亲,再让父亲删掉他)
新方法
childNode.remove()
- 如果一个
node
被移出页面(DOM树
),那么它还可以再次回到页面中(appendChild()
)。因为他只是从页面中被删除了,他还在js引擎线程中
改
改标准属性
改class
div.className= 'red blue'
(全覆盖)div.className += 'black'
(增加)div.classList.add('black')
(增加) 改stylediv.style = 'width: 100px; color: blue;'
(全覆盖)- 改style的一部分:
div.style.width = '200px'
- 大小写:
div.style.backgroundColor = 'white'
*改data-属性: div.dataset.x = 'frank'
(没人用了)
//在html里给元素加上属性data-xxx='yyy'
<div id="day2-meal-expense"
data-drink="coffee"
data-food="sushi"
data-meal="lunch">¥20.12</div>
//要想获取某个data-*属性的值,可以像下面这样使用dataset对象:
var expenseday2 = document.getElementById('day2-meal-expense');
var typeOfDrink = expenseday2.dataset.drink; //'coffee'
读标准属性
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
这个不会,只是元素节点,优先使用- 当子代变化时,两者也会实时变化
document.querySelectorAll('.red')[0]
不会实时更新
查兄弟姐妹
node.parentNode.childNodes
但自己也在里面,要排除自己和所有文本节点node.parentNode.children
但自己也在里面,要排除自己- 排除自己:要用for循环遍历所有子节点,然后把自己排除出去
let siblings = []
let arr = div2.parentElement.children //找出所有子节点形成一个数组
for(let i=0;i<c.length;i++){ //遍历数组中每个元素
if(arr[i] !== div2){ //如果不是自己
sibling.push(arr[i]) //就加入到新数组中
}
}
查老大
node.firstChild
查老小
node.lastChild
查看上一个哥哥/姐姐
node.previousSibling
查看下一个弟弟/妹妹
node.nextSibling
遍历一个div里面的所有元素(查看一个节点里所有的元素(儿子、孙子、曾孙子,,,))
let travel = (node,fn) => {
fn(node)
if(node.children){
for(let i = 0;i<node.children.length;i++){
travel(node.children[i],fn)
}
}
}
travel(d1,(node)=>{console.log(node)})
六、DOM操作是跨线程的
跨线程操作
各线程各司其职
- JS引擎不能操作页面,只能操作JS
- 渲染引擎不能操作JS,只能操作页面
跨线程通信
document.body.appendChild(div1)
---这是一句跨线程的DOM操作- 当浏览器发现JS在
body
里面加了个div1对象
- 浏览器就会通知渲染引擎在页面里也新增一个
div元素
- 新增的
div元素
所有属性都照抄div1对象
插入新标签的完整过程
在div1放入页面之前
- 你对div1所有的操作都属于JS线程内的操作
let div1 = document.createElement('div')
div1.textContent = 'hi'
把div1放入页面之时
- 浏览器会发现JS的意图,就会通知渲染线程在页面中渲染div1对应的元素
document.body.appendChild(div1)
把div1放入页面之后
- 你对
div1
的操作都有可能会触发重新渲染 div1.id = 'newid'
可能会重新渲染,也可能不会div1.title= 'new'
可能会重新渲染,也可能不会- 如果你连续对
div1
多次操作,浏览器可能会合并成一次操作,也可能不会(之前在动画里提到过)
属性同步
标准属性
- 对
div1
的标准属性的修改,会被浏览器同步到页面中 - 比如
id
、className
、title
等
data-*属性 同上
非标准属性
- 对非标准属性的修改,则只会停留在JS线程中,不会同步到页面里
- 比如x属性,示例代码
启示
- 如果你有自定义属性,又想被同步到页面中,请使用
data-
作为前缀
Property V.S. Attribute
- property属性:JS线程中div1对象的所有属性,叫做div1的property
- attribute也是属性:渲染引擎中div1元素的属性,叫做attribute
- 区别:
- 大部分时候,同名的property和attribute值相等
- 但如果不是标准属性(把div1放入页面之后只有标准属性会同步),那么它俩只会在一开始时相等(把div1放入页面之时,全过去)
- 但注意attribute只支持字符串,而property支持字符串、布尔等类型