一、网页其实是一棵树
二、JS用document操作网页
html内容由渲染引擎来渲染到页面上,而JS引擎负责的是JS内容,是操作不了文档内容的,咋办? 于是让浏览器来提供办法。 浏览器往window上加一个document对象,这时JS就可以用document来操作网页了,这就是document object model,即文档对象模型(把网页抽象成对象,然后来操作它)
三、获取元素,也叫标签
获取任意元素
1.偷偷用的最简便的方法
window.idid- 但是如果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会被当做thisevent则包含了点击事件的所有信息,如坐标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支持字符串、布尔等类型