网页其实也是一棵树,Html标签为根节点,文字也是一个节点
js怎么操作网页?
浏览器给window加了个document,window.document可以拿到根节点,根节点里所有的元素,就可以用document来操作,DOM Document Object Model 文档对象模型
如何获取任意元素? Element 元素/标签Tag
window.id/id
做demo直接用
window.logo
logo
document.getElementById(‘id’)
不用加#号 和全局属性冲突时用
document.getElementById('logo')
document.getElementsByTagName(‘标签名’)[0]
是一个伪数组,可加下标索引
document.getElementsByTagName('div')[5]
document.getElementsByClassName(‘类名’)[0]
是一个数组,可加下标索引
document.getElementsByClassName('inited')[0] //类不用加.
document.querySelector(’#id’)
id必须加# 获取第一个 选择器都可
document.querySelector('#logo')
document.querySelectorAll(’.inited’)[0]
是一个数组,可加下标索引,类加.,id加#
document.querySelectorAll('#logo')[0]
如何获取特定元素?
只获取HTML元素 document.documentElement
只获取head document.head
<head>…</head>
只获取body document.body
<body class="light-chip alternate-logo use-notifier win" style="--logo-color:rgba(238,238,238,1);">…</body>
获取窗口window
window虽然不是元素也不是标签,但是可以控制
Window {parent: Window, opener: null, top: Window, length: 4, frames: Window, …}
监听事件
window.onclick=()=>{console.log('hi')}
hi
获取所有元素:document.all
是第六个falsy值 常用于判断是否为ie浏览器
document.all?'真':'假'
"假"
获取的元素是对象,需要搞懂他的原型
div六层原型链
console.dir打出所有目录,查看原型链
console.dir(document.querySelectorAll('div')[5])
- 一层原型:HTMLDivElement.prototype
- 二层原型:HTMLElement.prototype
- 三层原型:Element.prototype 所有XML\HTML标签
- 四层原型:Node.prototype 所有XML\HTML标签,文本,注释等
- 五层原型:EventTarget.prototype 活动目标
- 六层原型:Object.prototype
节点Node有几种
1 标签 元素
3 文本 文字
8 注释
9 文档
11 文档片段
节点的增删改查
增
增加标签节点
let div1=document.createElement(‘div/script/li等等’)
let div5=document.createElement('div')
div5
<div></div>
添加文本节点
let text1=document.createTextNode(‘你好’)
let c=document.createTextNode('你好')
c
"你好"
在标签里加内容
div1.appendChild(text1) Node接口 text1是变量
div1.innerText(“文本”) div1.textContent(“文本”) Element接口
div5.innerText='你好'
div5
<div>你好</div>
div5.textContent='你好'
div5
<div>你好</div>
div5.appendChild(c)
div5
<div>你好 "你好"</div>
不可混用Node接口和Element接口,否则报错
let div7=document.createElement('div')
div7.appendChild('你好')
VM55442:1 Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
at <anonymous>:1:6
把标签插入页面
document.body.appendChild(div5) 必须插入才显示
div5.style.xxx来修改部分样式
创建的标签默认显示在js内,只有插入到head,body中才有效
document.body.append(div5)
div5.style.color='red'
"red"
div5.style.position='fixed'
"fixed"
div5.style.top='0px'
"0px"
例子:把div5先插入页面中body,再插入head中,只会在head中显示,由于他只能有一个父亲,类比送子观音
深拷贝,把div5复制一份,可以同时在head body显示
div6=div5.cloneNode(true)
document.head.appendChild(div6)
删
老方法
parentNode.removeChild(childNode) 可返回,由于删的还存在内存中,链进入
div5.parentNode.removeChild(div5)
div5.parentNode.appendChild(div5)
新方法 不兼容IE
childNode.remove() 可返回,由于删的还存在内存中
div5.remove()
彻底删除
div5.remove()之后
div5=null
改
写标准属性
改id div5.id=‘div3’
加引号 给div里加id
改class div5.className=‘xx’
给div里加类,class关键字
div5.classList.add(‘red’)
得class=“red blue”
改style id.style=‘color:black’
会替换掉全部
改style部分内容 id.style.color=‘black’
大小写 id.style.backgroundColor=‘white’
其中-的,都省略-减号,加字母大写
改data-*属性 有- 的属性名
- 向div5里添加,注意是分号 div5.setAttribute(‘data-x’,‘test’)
- 查看data-*属性值:div5.dataset.x
- 修改data-*属性值:div5.dataset.x=‘frank’
div5.setAttribute('data-x','test')
div5
<div style="position: fixed; top: 0px; font-size: 60px; color: black; background-color: white;" id="divx" class="xx" data-x="test">你好</div>
div5.dataset.x
"test"
div5.dataset.x='frank'
"frank"
div5.dataset.x
"frank"
读标准属性
div.classList/a.href 有时出错,例如a标签
**div.getAttribute(‘class’) a.getAttribute(‘href’)**更保险
a.href
"http://js.jirengu.com/xxx"
a.getAttribute('href')
"xxx
a.className
"name"
a.getAttribute('class')
"name"
改事件处理函数
onclick点击
默认是null,给onclick设成一个函数,点击时调用fn(this,event),浏览器点击,用call传进来的
div5.onclick=function(){console.log(this);console.log(x)}
div.addEventListener监听多个
改内容
- 改文本内容 div5.innerText=‘修改’ div5.textContent=‘修改2’
- 改标签内容 **div5.innerHTML ** 但是也限制不能过多
div5.innerHTML='<strong>加粗</strong>'
- 改标签 先清空div5.innerHTML=’'再加内容div5.innerText=‘哈哈哈哈’
改爸爸
新的父亲节点.appendChild(子元素)
div5
let div2=document.createElement('div')
div2.appendChild(div5)
查
查爸爸
div5.parentNode或者div5.parentElement
查爷爷
div5.parentElement.parentElement
查后代
x.children 常用这个,不含文本节点,删除后长度变
x.childNodes 会把空格文本算进去,删除后长度变
x.querySelectorAll(‘li’).remove 删除后不会改变长度
<ul id='x'>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
x.children.length
3
x.childNodes.length
7
查兄弟姐妹
node.parsentNode.childNodes 包含自己和空格文本
node.parsentNode.children 包含自己
获取兄弟姐妹两个步骤:获取到父亲所有的孩子,在所有孩子中排出自己
a=[]
let arr=div5.parentNode.children
console.log(arr)
console.log('----')
for(let j=0;j<arr.length;j++){
if(arr[j]!==div5){
a.push(arr[j])
}
}
a
[div]
查看老大 div2.firstChild
查看老么 div2.lastChild
查看上一个元素哥哥 div4.previousElementSibling
只有元素 不带Element含有文本空格
参看下一个元素弟弟 div5.nextElementSibling
只有元素 不带Element含有文本空格
遍历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(div2,(node)=>console.log(node))
DOM操作跨线程
js引擎只能操作js,如document对象
渲染引擎只能操作页面,如Html.css
但是document.body.appendChild(‘div’)这个js怎么操作页面的呢?
跨线程通信
- 浏览器发现js在body里加一个div对象
- 便通知渲染引擎在页面上加div元素,是浏览器渲染的,不是js渲染的
- 然后新增的元素的属性都照搬div对象
分析跨线程操作
所以浏览器通知会浪费时间,但是也方面单个优化
let div1=document.createElement('div')
div1.textContent='哈喽'
document.body.appendChild(div1)
div1.textContent='嘻嘻嘻'
插入新标签div1的全部过程
-
放入页面之前:前两个操作只会在js执行线程里操作
-
放在页面之时:浏览器发现js线程要向body里加div1,于是通知渲染线程在页面渲染div1对应的元素
-
放入页面之后://浏览器通知渲染线程修改页面内容,可能会触发重新渲染
div.id=‘newId’ 单操作可能重新渲染
dib.title=‘new’ 单操作可能重新渲染
多次操作div,可能合并成一个操作,比如动画:x.clientWidth触发重新渲染
.start{ width: 200px; height: 100px; border:1px solid red; transition:width 1s; } .end{ width: 100px; } x.classList.add('start') x.clientWidth//这条可以实现动画显示,避免渲染合并 x.classList.add('end')
属性同步
<div id="test" x="test" data-x="test">
let div1 = document.querySelector('#test')
div1.id = 'frank' // 同步过去了
div1.dataset.x = 'frank' // 同步过去了
div1.x = 'frank' // 没有同步过去
div1.style.border = '1px solid red'
<div id="frank" x="test" data-x="frank">//结果是x改变
标准属性
修改会被浏览器同步到页面中 如className、title、id
data-*属性
修改会被浏览器同步到页面中
非标准属性
修改后不会被浏览器同步到页面上,只会留在js线程里
启发
非标准属性前面加data-做前缀
properties VS attributes
prototype属性:js里的div的所有属性,常是对象的,如style,id…
attribute属性:渲染引擎中div对应标签属性,比如id='字符串'
这条是属性
区别:同名基本相等,如果不是标准属性,开始时相等,修改后不一定相等
attribute只支持字符串,property支持很多类型