目录
前言
本章学习DOM,包含各个节点,文档的加载,DOM的修改等。在事件对象中将学习事件的捕获、冒泡。
一、DOM
Document Object Model
文档对象模型,通过DOM可以来任意来修改网页中各个内容,DOM把JS与网页联系起来
文档 文档指的是网页,一个网页就是一个文档
对象 对象指DOM将网页中的每一个节点都转换为对象, 转换完对象以后,就可以以一种纯面向对象的形式来操作网页了
模型 模型用来表示节点和节点之间的关系,方便操作页面
节点(Node) 节点是构成网页的最基本的单元,网页中的每一个部分都可以称为是一个节点。虽然都是节点,但是节点的类型却是不同的。
节点是网页所有对象的父类
常用的节点
- 文档节点 (Document),代表整个网页
- 元素节点(Element),代表网页中的标签
- 属性节点(Attribute),代表标签中的属性
- 文本节点(Text),代表网页中的文本内容
要使用DOM来操作网页,我们需要浏览器至少得先给我一个对象
浏览器已经为我们提供了一个document对象,它是一个全局变量可以直接使用
document代表的是整个的网页
文档节点
document对象表示的是整个网页,它是window对象的属性,可以在页面中直接使用
document对象的原型链
HTMLDocument -> Document -> Node -> EventTarget -> Object.prototype -> null
凡是在原型链上存在的对象的属性和方法都可以通过Document去调用
部分属性:
document.documentElement --> html根元素
document.head --> head元素
document.title --> title元素
document.body --> body元素
document.links --> 获取页面中所有的超链接
元素节点
在网页中,每一个标签都是一个元素节点
如何获取元素节点对象?
1. 通过document对象来获取元素节点
2. 通过document对象来创建元素节点
1.通过document来获取已有的元素节点:
document.getElementById()
根据id获取一个元素节点对象
document.getElementsByClassName()
根据元素的class属性值获取一组元素节点对象
返回的是一个类数组对象
该方法返回的结果是一个实时更新的集合
当网页中新添加元素时,集合也会实时的刷新
document.getElementsByTagName()
根据标签名获取一组元素节点对象
返回的结果是可以实时更新的集合
document.getElementsByTagName("*") 获取页面中所有的元素
document.getElementsByName()
根据name属性获取一组元素节点对象
返回一个实时更新的集合
主要用于表单项
document.querySelectorAll()
根据选择器去页面中查询元素
会返回一个类数组(不会实时更新)
document.querySelector()
根据选择器去页面中查询第一个符合条件的元素
2.创建一个元素节点
document.createElement()
根据标签名创建一个元素节点对象
div元素的原型链
HTMLDivElement -> HTMLElement -> Element -> Node -> ...
通过元素节点对象获取其他节点的方法
element.childNodes 获取当前元素的子节点(会包含空白的子节点,包含文本和换行)
element.children 获取当前元素的子元素 (这个比较合理,用这个)
element.firstElementChild 获取当前元素的第一个子元素
element.lastElementChild 获取当前元素的最后一个子元素
element.nextElementSibling 获取当前元素的下一个兄弟元素
element.previousElementSibling 获取当前元素的前一个兄弟元素
element.parentNode 获取当前元素的父节点
element.tagName 获取当前元素的标签名
<div id="box1">
我是box1
<span class="s1">我是s1</span>
<span class="s1">我是s2</span>
</div>
<script>
const box1 = document.getElementById("box1")
const cns = box1.childNodes
</script>
文本节点
在DOM中,网页中所有的文本内容都是文本节点对象
可以通过元素来获取其中的文本节点对象,但是我们通常不会这么做,可以直接通过元素操作它们,无需获取
我们可以直接通过元素去修改其中的文本
element.textContent 获取或修改元素中的文本内容
获取的是标签中的内容,不会考虑css样式
element.innerText 获取或修改元素中的文本内容
innerText获取内容时,会考虑css样式
通过innerText去读取CSS样式,会触发网页的重排(计算CSS样式),性能会差一点
上述修改元素文本内容,当字符串中有标签时,会自动对标签进行转义,<li> --> <li>
element.innerHTML 获取或修改元素中的html代码
可以直接向元素中添加html代码
innerHTML插入内容时,有被xss注入的风险,避免从用户那接收内容来修改html代码
<div id="box1">
<span style="text-transform: uppercase;">我是box1</span>
</div>
<script>
const box1 = document.getElementById("box1")
</script>
属性节点
在DOM也是一个对象,通常不需要获取对象而是直接通过元素即可完成对其的各种操作
如何操作属性节点:
方式一:
读取:元素.属性名
注意:class属性需要使用className来读取
读取一个布尔值时,会返回true或false
修改:元素.属性名 = 属性值
方式二:
读取:元素.getAttribute(属性名)
修改:元素.setAttribute(属性名, 属性值)
删除:元素.removeAttribute(属性名)
<input class="a" type="text" name="username" value="admin">
<script>
const input = document.getElementsByName("username")[0]
console.log(input.type)
console.log(input.className)
</script>
事件
事件就是用户和页面之间发生的交互行为
比如:点击按钮、鼠标移动、双击按钮、敲击键盘、松开按键...
可以通过为事件绑定响应函数(回调函数),来完成和用户之间的交互
绑定响应函数的方式:
1.可以直接在元素的属性中设置
2.可以通过为元素的指定属性设置回调函数的形式来绑定事件(一个事件只能绑定一个响应函数)会覆盖
3.可以通过元素addEventListener()方法来绑定事件,一个事件能绑定多个响应函数
<button id="btn">点我一下</button>
<script>
// 获取到按钮对象
const btn = document.getElementById("btn")
//方法1
<button id="btn1" onmouseenter="alert('你点我干嘛~')">点我一下</button>
//方法2
btn.onclick = function(){
alert("我又被点了一下~~")
}
btn.onclick = function(){
alert("1123111")
}
//方法3
btn.addEventListener("click", function(){
alert("哈哈哈")
})
btn.addEventListener("click", function(){
alert("嘻嘻嘻")
})
</script>
文档的加载
网页是自上向下加载的,如果将js代码编写到网页的上边,js代码在执行时,网页还没有加载完毕,这时会出现无法获取到DOM对象的情况
如何解决这个问题:
1. 将script标签编写到body的最后
2. 将代码编写到window.onload的回调函数中
3. 将代码编写到document对象的DOMContentLoaded的回调函数中(执行时机更早)
4. 将代码编写到外部的js文件中,然后以defer的形式进行引入(执行时机更早,早于DOMContentLoaded)
window.onload 事件会在窗口中的内容加载完毕之后才触发(包括如果使用到的其他文档加载完)
document的DOMContentLoaded事件会在当前文档加载完毕之后触发
//方法2
window.onload = function () {
const btn = document.getElementById("btn")
console.log(btn)
}
window.addEventListener("load", function () {
const btn = document.getElementById("btn")
alert(btn)
})
//方法3
document.addEventListener("DOMContentLoaded", function () {
const btn = document.getElementById("btn")
alert(btn)
})
//方法4
<script defer src="./script/script.js"></script>
DOM的修改
1、增
appendChild() 用于给一个节点添加子节点
insertAdjacentElement()可以向元素的任意位置添加元素
两个参数:1.要添加的位置 2.要添加的元素
beforeend 标签的最后 afterbegin 标签的开始
beforebegin 在元素的前边插入元素(兄弟元素) afterend 在元素的后边插入元素(兄弟元素)
方法:
list.appendChild(li)
list.insertAdjacentElement("afterend", li)
list.insertAdjacentHTML("beforeend", "<li id='bgj'>白骨精</li>")
const list = document.getElementById("list")
// 获取按钮
const btn01 = document.getElementById("btn01")
btn01.onclick = function () {
// 创建一个li
const li = document.createElement("li")
// 向li中添加文本
li.textContent = "唐僧"
// 给li添加id属性
li.id = "ts"
list.appendChild(li)
list.insertAdjacentElement("afterend", li)
list.insertAdjacentHTML("beforeend", "<li id='bgj'>白骨精</li>")
}
2、删
remove()方法用来删除当前元素
swk.remove()
3、改
replaceWith() 使用一个元素替换当前元素 被替换元素.replaceWith(替换元素)
const btn02 = document.getElementById("btn02")
btn02.onclick = function(){
// 创建一个蜘蛛精替换孙悟空
const li = document.createElement("li")
li.textContent = "蜘蛛精"
li.id = "zzj"
// 获取swk
const swk = document.getElementById("swk")
swk.replaceWith(li)
}
事件中可以通过取消默认行为来阻止超链接的跳转,使用return false来取消默认行为,只在 xxx.xxx = function(){}这种形式绑定的事件中才适用
节点的复制
使用 cloneNode() 方法对节点进行复制时,它会复制节点的所有特点包括各种属性
这个方法默认只会复制当前节点,而不会复制节点的子节点(例如里面的文本节点就不会复制)
可以传递一个true作为参数,这样表示既复制当前节点也复制节点的子节点
const btn01 = document.getElementById("btn01")
btn01.onclick = function () {
const newL1 = l1.cloneNode(true) // 用来对节点进行复制的
newL1.id = "newL1"
list2.appendChild(newL1)
}
CSS样式
修改
1、元素.style.样式名 = 样式值【少用,要有单位】
如果样式名中含有'-',则需要将样式名修改为驼峰命名法:background-color --> backgroundColor
这样修改的样式相当于给元素添加内联样式
2、修改class属性来间接修改样式
元素.classList 是一个对象,对象中提供了对当前元素的类的各种操作方法
元素.classList.add() 向元素中添加一个或多个class
元素.classList.remove() 移除元素中的一个或多个class
元素.classList.toggle() 切换元素中的class
元素.classList.replace("旧","新") 替换class
元素.classList.contains() 检查class
box1.className += " box2" //不建议
box1.classList.add("box2", "box3", "box4")
box1.classList.add("box1")
box1.classList.remove("box2")
box1.classList.toggle("box2")
box1.classList.replace("box1", "box2")
let result = box1.classList.contains("box3")
console.log(result)
通过class修改样式的好处:
可以一次性修改多个样式
对JS和CSS进行解耦
读取
getComputedStyle(参数1,"参数2")
它会返回一个对象,这个对象中包含了当前元素所有的生效的样式
参数:
1. 要获取样式的对象
2. 要获取的伪元素
<style>
.box1 {
height: 200px;
background-color: #bfa;
}
.box1::before {
content: "hello";
color: red;
}
</style>
<script>
const btn = document.getElementById("btn")
const box1 = document.querySelector(".box1")
btn.onclick = function () {
const styleObj = getComputedStyle(box1)
console.log(styleObj.backgroundColor)
console.log(styleObj.width)
console.log(styleObj.left)
//两个参数
const beforeStyle = getComputedStyle(box1, "::before")
console.log(beforeStyle.color)
//要先转
console.log(parseInt(styleObj.width) + 100)
box1.style.width = parseInt(styleObj.width) + 100 + "px"
}
</style>
注意:样式对象中返回的是带单位样式值,一定不能来拿来直接计算,所以在计算时,一定要进行类型转换
样式对象中返回的样式值,不一定能来拿来直接计算,所以使用时,一定要确保值是可以计算的才去计算,因为有些返回auto
元素.属性(返回的数值没有单位,可以直接使用)
1、获取元素内部的宽度和高度(包括内容区和内边距)
元素.clientHeight
元素.clientWidth
2、获取元素的可见框的大小(包括内容区、内边距和边框)
元素.offsetHeight
元素.offsetWidth
3、获取元素滚动区域的大小
元素.scrollHeight
元素.scrollWidth
4、获取元素的定位父元素
元素.offsetParent
定位父元素:离当前元素最近的开启了定位的祖先元素,如果所有的元素都没有开启定位则返回body
5、获取元素相对于其定位父元素的偏移量
元素.offsetTop
元素.offsetLeft
6、获取或设置元素滚动条的偏移量
元素.scrollTop
元素.scrollLeft
二、事件对象
事件对象是由浏览器在事件触发时所创建的对象,这个对象中封装了事件相关的各种信息
浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,所以我们可以在事件的回调函数中定义一个形参来接收事件对象,与你定义的函数形式无关
const box1 = document.getElementById("box1")
box1.onmousemove = function(){
console.log(event)
}
box1.onmousemove = event => {
console.log(event)
}
box1.addEventListener("mousemove", event => {
console.log(event.clientX, event.clientY)
box1.textContent = event.clientX + "," + event.clientY
})
多种事件对象有一个共同的祖先 Event
event.target 触发事件的对象(不等于绑定事件的对象,this是指向绑定事件的对象)
event.currentTarget 绑定事件的对象(同this)
event.stopPropagation() 停止事件的传导
event.preventDefault() 取消默认行为,代替return false那种法子
事件的冒泡(bubble)
事件的冒泡就是指事件的向上传导
当元素上的某个事件被触发后,其祖先元素上的相同事件也会同时被触发
冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在 不希望事件冒泡时,可以通过事件对象来取消冒泡 event.stopPropagation() 取消事件传导
事件的冒泡与样式无关,只与结构有关(结构上重叠,虽然样式上不重叠也会触发冒泡)
事件的捕获
指事件从外向内的传导,当前元素触发事件以后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获
如果希望在捕获阶段触发事件,可以将addEventListener的第三个参数设置为true
一般情况下我们不希望事件在捕获阶段触发,所以通常都不需要设置第三个参数
事件的委派
委派就是将本该绑定给多个元素的事件,统一绑定给document(一般),这样可以降低代码复杂度方便维护
在document的触发事件通过写条件判断来筛选相关元素
事件的传播机制
在DOM中,事件的传播可以分为三个阶段:event.eventPhase 表示事件触发的阶段
捕获阶段 (由祖先元素向目标元素进行事件的捕获)(默认情况下,事件不会在捕获阶段触发)
目标阶段 (触发事件的对象)如果事件指定不冒泡,那就会在这里中止
总结
本章学习了重点内容文档对象模型DOM以及事件。
根据尚硅谷李立超老师2022年的JS视频整理而成。