js中最重要的就是对DOM的操作
要想操作DOM,我们先理解以下几个名称的意思
- 元素就是标签,只是叫法不同;< p >可以称为p标签,也可以叫p元素
- 节点包括元素和文本;通俗的来说:p标签和它里面的文字,合一块叫做节点
操作DOM元素就是对节点的增删改查
什么是 DOM?
DOM就是文档对象模型 ,一个网页就可以看作一个文档
DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合;它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。
获取元素的几种方式
<p id="p1" class="p2">Hello World!</p>
//获取p元素的几种方式
document.getElementById("p1") :根据ID查找元素,大小写敏感,如果有多个结果,只返回第一个;
document.getElementsByClassName("p2") :根据类名查找元素,多个类名用空格分隔,返回一个数组
document.getElementsByTagName("p") :根据标签查找元素, * 表示查询所有标签,返回一个 HTMLCollection 。
document.getElementsByName :根据元素的name属性查找,返回一个 NodeList 。
document.querySelector("#p1") :括号里面是#id名或者.class名;返回单个Node,IE8+(含),如果匹配到多个结果,只返回第一个。
document.querySelectorAll() :返回一个 NodeList ,IE8+(含)。
这就是打印出来的元素
操作DOM
创建元素
创建一个节点,并把它添加到页面中
//创建一个标签
let div1 = document.createElement('div')
//创建一个文本节点
text1 = document.createTextNode('你好')
//把文本插入标签节点
div1.appendChild(txet1)
你创建的标签默认处于JS线程中
你必须把它插到head或者body里面,它才会生效
document.body.appendChild('div1')
或者插入已在页面中的元素
已在页面中的元素.appendChild('div1')
同一个标签不能同时插入到两个页面标签里,如果想同时存在,需要克隆
参数为true时是深拷贝,会把div1里的所有后代全都复制一遍
不写默认是浅拷贝,只复制此节点
let div2 = div1.cloneNode(true)
删除
删除有两种方法
新方法
父元素.childChild(子元素)
老方法
要移除的元素.remove()
改
修改属性
写标准属性
改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 ="dong"
读标准属性
div.classList/a.href
div.getAttribute('class')/a.getAttribute('href')
两种方法都可以,但值可能稍微有些不同
改内容
改文本内容
div.innerText ='xxx'
div.textContent='xxx'
两者几乎没有区别
改HTML内容
div.innerHTML='<strong>重要内容</strong>'
改标签
div.innerHTML="//先清空
div.appendChild(div2)//再加内容
标签.childNodes.length 是计算标签的子元素的(回车也算)
标签.children.length 是计算子元素(不含回车)
查
查爸爸
node.parentNode 或者node.parentElement
查爷爷
node.parentNode.parentNode
查子代
node.childNodes或者node.children
查兄弟姐妹
node.parentNode.childNodes还要排除自己
node.parentNode.children还有排除自己
遍历一个div里所有的元素
travel = (node, fn) =>{
fn(node)
if(node.children){
for(let i=0; node.children.length;i++){
travel(node.children[i], fn)
}
}
}
travel(div1, (node) => console.log(node))
DOM捕获阶段和冒泡阶段
DOM事件流:事件流描述的是从页面中接收事件的顺序。(事件传播的过程就是事件流)
包括三个阶段:
事件捕获阶段:该阶段的主要作用是捕获截取事件
处于目标阶段:一般地,该阶段具有双重范围,即捕获阶段的结束,冒泡阶段的开始;
事件冒泡阶段:主要作用是将目标元素绑定事件执行的结果返回给浏览器,处理不同浏览器之间的差异,主要在该阶段完成
捕获 当用户点击按钮,浏览器会从 window 从上向下遍历至用户点击的按钮,逐个触发事件处理函数。
冒泡 浏览器从用户点击的按钮从下往上遍历至 window,逐个触发事件处理函数。
什么是捕获阶段?
当监听事件 addEventLister
的第三个参数为true则处于捕获阶段
事件捕获阶段:如果上级有事件,则就会执行先执行上级的事件,再执行点击事件(执行顺序为:爷爷事件——>爸爸事件——>儿子事件)
什么是冒泡阶段?
第三个参数不填或为false
事件冒泡阶段:先执行被点击的元素事件,再一层一层执行上级事件(执行顺序为:被点击事件——>爸爸事件——>爷爷事件)
是想执行冒泡阶段和还是捕获阶段?
//IE浏览器是执行冒泡阶段
IE5*:baba.attachEvent('onclick',fn)
//捕获
网景:baba.addEventListener('click';fn)
//现在都使用w3c的标准,由第三个参数控制冒泡还是捕获,不填或者false就是冒泡阶段
W3C:baba.addEventListener('click;,fn,bool)
阻止冒泡的方法
使用 e.stopPropagation()
可以中断冒泡
但是有些事件此方法没有效果,比如说 scroll滚动事件
阻止滚动事件的方法
阻止scroll默认动作没有用,因为有滚动才有滚动事件,要想阻止滚动,需要阻止whell和touchstart的默认动作
禁用滚动事件
元素.addEventListener('whell',(e)=>{
e.preventDefault()
})
此时使用鼠标滚轮没有用了,但是还有滚动条
把滚动条隐藏
::-webkit-scrollbar {
width: 0 !important
}
禁用手机端的滚动事件
元素.addEventListener('touchstart',(e)=>{
e.preventDefault()
})
事件委托
当监听子元素时,事件冒泡会通过目标元素向上传递到父级,直到document
,如果子元素不确定或者动态生成,可以通过监听父元素来取代监听子元素。
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element)
}
element = addEventListener(eventType, (e) => {
const t = e.target
if (t.matches(selector)) {
fn(e)
}
})
}
on('click', '#div1', 'button', () => {
console.log('buton被点击了')
})
target与currentTarget
e 会在事件结束之后自动消亡,如果在setTimeout定时操作里使用e,则会报错:如下代码则会报错
x1.addEventListener('click', (e) => {
setTimeout(() => {
e.currentTarget.classList.remove('x')
}, 1000)
})
解决方法:就是把e记下来
x1.addEventListener('click', (e) => {
const t = e.currentTarget
setTimeout(() => {
t.classList.remove('x')
}, 1000)
})
target是被操作(被点击)的元素
currentTarget是程序员监听的元素