DOM,全称Document Object Model,中文翻译为文档对象模型。DOM属于Web API的一部分。Web API中定义了非常多的对象,通过这些对象可以完成对网页的各种操作(添加删除元素、发送请求、操作浏览器等)。
什么是DOM {#%E4%BB%80%E4%B9%88%E6%98%AFdom}
DOM中的D意为Document,即文档。所谓文档就是指整个网页,换言之,DOM是用来操作网页的。O意为Object,即对象。DOM将网页中的每一部分内容都转换为了对象,div有div的对象,input有input的对象,甚至一段文本,一段注释也有其所对应的对象。转换为对象干什么?还记得面向对象吗?转换对象以后,我们就可以以面向对象的方式去操作网页,想要操作哪个元素就获取哪个元素的对象,然后通过调用其方法或属性完成各种操作。M意为Model,即模型。模型用来表示对象之间的关系,也就是父子元素、祖先后代、兄弟元素等,明确关系后我们便可以通过任意一个对象去获取其他的对象。
<!DOCTYPE html>
<html lang="zh">
<head>
<title>My Title</title>
</head>
<body>
<h1>A Heading</h1>
<a href="#">Link Text</a>
</body>
</html>
>概念 {#%3E%E6%A6%82%E5%BF%B5}
节点(Node) {#%E8%8A%82%E7%82%B9%EF%BC%88node%EF%BC%89}
在DOM标准下,网页中的每一个部分都会转换为对象。这些对象有一个共同的称呼------节点(Node)。一个页面将会由多个节点构成,虽然都称为节点,但是它们却有着不同的类型:
-
文档节点
-
元素节点
-
文本节点
-
属性节点
-
...
每一个节点都有其不同的作用,文档节点表示整个网页,元素节点表示某个标签,文本节点表示网页中的文本内容,属性节点表示标签中的各种属性。如果从对象的结构上来讲,这些对象都有一个共同的父类Node。总的来说,都是属于节点,但是具体类型不同。
关系 {#%E5%85%B3%E7%B3%BB}
-
祖先 ------ 包含后代元素的元素是祖先元素
-
后代 ------ 被祖先元素包含的元素是后代元素
-
父 ------ 直接包含子元素的元素是父元素
-
子 ------ 直接被父元素包含的元素是子元素
-
兄弟 ------ 拥有相同父元素的元素是兄弟元素
小试牛刀 {#%E5%B0%8F%E8%AF%95%E7%89%9B%E5%88%80}
要使用DOM来操作网页,我们需要浏览器至少得先给我一个对象,才能去完成各种操作。所以浏览器已经为我们提供了一个document对象,它是一个全局变量可以直接使用,document代表的是整个的网页。
<button id="btn">点我一下</button>
\<script\>
// 获取btn对象
const btn = document.getElementById("btn")
`// 修改btn中的文字
btn.innerText = "Click ME"
</script>`
>文档节点(document) {#%3E%E6%96%87%E6%A1%A3%E8%8A%82%E7%82%B9%EF%BC%88document%EF%BC%89}
-
document对象表示的是整个网页
-
document对象的原型链
HTMLDocument -> Document -> Node -> EventTarget -> Object.prototype -> null
-
凡是在原型链上存在的对象的属性和方法都可以通过Document去调用
-
部分属性:
document.documentElement --> html根元素
document.head --> head元素
document.title --> title元素
document.body --> body元素
document.links --> 获取页面中所有的超链接
...
>元素节点(element) {#%3E%E5%85%83%E7%B4%A0%E8%8A%82%E7%82%B9%EF%BC%88element%EF%BC%89}
- 在网页中,每一个标签都是一个元素节点
如何获取元素节点对象?
-
通过document对象来获取元素节点
-
通过document对象来创建元素节点
通过document来获取已有的元素节点:
document.getElementById() {#document.getelementbyid()}
- 根据id获取一个元素节点对象
<button id="btn">点我一下</button>
<script>
const btn = document.getElementById("btn")
console.log(btn)
</script>
document.getElementsByClassName() {#document.getelementsbyclassname()}
- 根据元素的class属性值获取一组元素节点对象
<span class="s1">我是span</span>
<span class="s1">我是span</span>
<span class="s1">我是span</span>
<span class="s1">我是span</span>
<span class="s1">我是span</span>
\<script\>
// 返回的是一个类数组对象,该方法返回的结果是一个实时更新的集合
// 当网页中新添加元素时,集合也会实时的刷新。
const spans = document.getElementsByClassName("s1")
console.log(spans) // HTMLCollection(5) \[span.s1, span.s1, span.s1, span.s1, span.s1\]
for (let i = 0; i \< spans.length; i++) {
spans\[i\].innerText = "我是span"+i
}
`</script>`
document.getElementsByTagName() {#document.getelementsbytagname()}
- 根据标签名获取一组元素节点对象
<div>我是div</div>
<div>我是div</div>
<div>我是div</div>
<div>我是div</div>
<div>我是div</div>
\<script\>
// 返回的结果是可以实时更新的集合
const divs = document.getElementsByTagName("div")
// document.getElementsByTagName("") 获取页面中所有的元素
const divs = document.getElementsByTagName("")
`console.log(divs) // HTMLCollection(5) [div, div, div, div, div]
</script>`
document.getElementsByName() {#document.getelementsbyname()}
- 根据name属性获取一组元素节点对象
<form>
<input type="text" name="username">
<input type="radio" name="gender" value="male"> 男
<input type="radio" name="gender" value="female"> 女
</form>
\<script\>
// 返回一个实时更新的集合,主要用于表单项。
const genderInput = document.getElementsByName("gender")
`console.log(genderInput) // NodeList(2) [input, input]
</script>`
document.querySelectorAll() {#document.queryselectorall()}
- 根据选择器去页面中查询元素
<div>我是div</div>
<div>我是div</div>
<div>我是div</div>
\<script\>
// 会返回一个类数组(不会实时更新)
const divs2 = document.querySelectorAll("div")
`console.log(divs2) // NodeList(3) [input, input,input]
</script>`
document.querySelector() {#document.queryselector()}
- 根据选择器去页面中查询第一个符合条件的元素
<div>我是div</div>
<div>我是div</div>
\<script\>
// 会返回一个类数组(不会实时更新)
const div = document.querySelector("div") // .xx \| #xx
console.log(divs2) // NodeList(2) \[input,input\]
`</script>`
创建一个元素节点
document.createElement() {#document.createelement()}
- 根据标签名创建一个元素节点对象
const h2 = document.createElement("h2") // 创建了H2,但未添加
>元素的属性和方法 {#%3E%E5%85%83%E7%B4%A0%E7%9A%84%E5%B1%9E%E6%80%A7%E5%92%8C%E6%96%B9%E6%B3%95}
<div id="box1">我是box1 </div>
\<script\>
const box1 = document.getElementById("box1")
cosole.log(box1.proto)
`</script>`
- div的原型链
HTMLDivElement -> HTMLElement -> Element -> Node -> ...
element.childNodes {#element.childnodes}
- 获取当前元素的子节点(会包含空白的子节点)
<div id="box1">
我是box1
<span class="s1">我是s1</span>
<span class="s1">我是s1</span>
</div>
\<script\>
const box1 = document.getElementById("box1")
const cns = box1.childNodes
console.log(children.length) // 输出:5 包含空格
`</script>`
element.children {#element.children}
- 获取当前元素的子元素
<div id="box1">
我是box1
<span class="s1">我是s1</span>
<span class="s1">我是s1</span>
</div>
\<script\>
const box1 = document.getElementById("box1")
const children = box1.children
console.log(children.length) // 输出:2(2个span)
`</script>`
element.firstElementChild {#element.firstelementchild}
- 获取当前元素的第一个子元素
<p>Hello world! This is HTML5 Boilerplate.</p>
<div id="box1">
我是box1
<span class="s1">我是s1</span>
<span class="s1">我是s1-2</span>
</div>
<h1>hello,world</h1>
\<script\>
const box1 = document.getElementById("box1")
console.log(box1.firstElementChild) //输出: \<span class="s1"\>我是s1\</span\>
`</script>`
element.lastElementChild {#element.lastelementchild}
- 获取当前元素的最后一个子元素
console.log(box1.lastElementChild) //输出: <span class="s1">我是s1-2</span>
element.nextElementSibling {#element.nextelementsibling}
- 获取当前元素的下一个兄弟元素
console.log(box1.nextElementSibling) //输出: <h1>hello,world</h1>
element.previousElementSibling {#element.previouselementsibling}
- 获取当前元素的前一个兄弟元素
console.log(box1.previousElementSibling) //输出: <p>Hello world! This is HTML5 Boilerplate.</p>
element.parentNode {#element.parentnode}
- 获取当前元素的父节点
console.log(box1.parentNode) //输出: <body>...</body>
element.tagName {#element.tagname}
- 获取当前元素的标签名
console.log(box1.tagName) //输出: DIV
>文本节点(Text) {#%3E%E6%96%87%E6%9C%AC%E8%8A%82%E7%82%B9%EF%BC%88text%EF%BC%89}
在DOM中,网页中所有的文本内容都是文本节点对象。
可以通过元素来获取其中的文本节点对象,但是我们通常不会这么做。
我们可以直接通过元素去修改其中的文本
修改文本的三个属性
element.textContent {#element.textcontent}
- 获取或修改元素中的文本内容
<div id="box1">
<span style="text-transform: uppercase;">我是box1</span>
</div>
\<script\>
// 获取的是标签中的内容,不会考虑css样式
const box1 = document.getElementById("box1")
box1.textContent = "新的内容"
`</script>`
element.innerText {#element.innertext}
- 获取或修改元素中的文本内容
<script>
// innerText获取内容时,会考虑css样式
// 通过innerText去读取CSS样式,会触发网页的重排(计算CSS样式)
const box1 = document.getElementById("box1")
box1.innerText = "新的内容"
// 当字符串中有标签时,会自动对标签进行转义
box1.innerText = "\<li\>我是li\</\>" // \<li\> --\> \<li\>
`</script>`
element.innerHTML {#element.innerhtml}
- 获取或修改元素中的html代码
<script>
// 可以直接向元素中添加html代码
const box1 = document.getElementById("box1")
box1.innerHTML = "XXX"
`// innerHTML插入内容时,有被xss注入的风险
box1.innerHTML = "<、script src='https://sss/sss.js'></script>"
</script>`
>属性节点(Attr) {#%3E%E5%B1%9E%E6%80%A7%E8%8A%82%E7%82%B9%EF%BC%88attr%EF%BC%89}
在DOM也是一个对象,通常不需要获取对象而是直接通过元素即可完成对其的各种操作。
如何操作属性节点:
方式一 {#%E6%96%B9%E5%BC%8F%E4%B8%80}
- 读取:元素.属性名(class属性需要使用 className 来读取)
读取一个布尔值时,会返回 true 或 false (如 disabled)
- 修改:元素.属性名 = 属性名
<input disabled="disabled" type="text" name="username" value="admin">
\<script\>
const input = document.getElementsByName("username")\[0\]
const input2 = document.querySelector("\[name=username\]")
console.log(input) // console.log(input.type)
input.name = "biu" // 修改:元素.属性名 = 属性名
`</script>`
方式二 {#%E6%96%B9%E5%BC%8F%E4%BA%8C}
-
读取:元素.getAttribute(属性名)
-
修改:元素.setAttribute(属性名, 属性值)
-
删除:元素.removeAttribute(属性名)
<input disabled="disabled" type="text" name="username" value="admin">
\<script\>
const input = document.getElementsByName("username")\[0\]
const input2 = document.querySelector("\[name=username\]")
input.getAttribute("name") // 读取:元素.getAttribute(属性名)
input.setAttribute("name","大王") // 修改:元素.setAttribute(属性名, 属性值)
input.setAttribute("disabled",true)
input.removeAttribute("disabled") // 删除:元素.removeAttribute(属性名)
`</script>`
>事件(Event) {#%3E%E4%BA%8B%E4%BB%B6%EF%BC%88event%EF%BC%89}
事件就是用户和页面之间发生的交互行为。例如:点击按钮、鼠标移动、双击按钮、敲击键盘、松开按键....
可以通过为时间绑定响应函数(回调函数),来完成和用户之间的交互。
绑定响应函数
直接在元素的属性中设置 {#%E7%9B%B4%E6%8E%A5%E5%9C%A8%E5%85%83%E7%B4%A0%E7%9A%84%E5%B1%9E%E6%80%A7%E4%B8%AD%E8%AE%BE%E7%BD%AE}
<button id="btn" onclick="alert('你点我干嘛~')">点我一下</button>
为元素指定属性设置(回调函数)来绑定事件 (一个事件只能绑定一个响应函数) {#%E4%B8%BA%E5%85%83%E7%B4%A0%E6%8C%87%E5%AE%9A%E5%B1%9E%E6%80%A7%E8%AE%BE%E7%BD%AE(%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0)%E6%9D%A5%E7%BB%91%E5%AE%9A%E4%BA%8B%E4%BB%B6-%EF%BC%88%E4%B8%80%E4%B8%AA%E4%BA%8B%E4%BB%B6%E5%8F%AA%E8%83%BD%E7%BB%91%E5%AE%9A%E4%B8%80%E4%B8%AA%E5%93%8D%E5%BA%94%E5%87%BD%E6%95%B0%EF%BC%89}
<script>
// 获取到按钮对象
const btn = document.getElementById("btn")
// 为按钮对象的事件属性设置响应函数
btn.onclick = function(){
alert("你点我干嘛~")
}
</script>
通过元素 addEventListener( )方法来绑定事件 (可绑定多个,会依次执行) {#%E9%80%9A%E8%BF%87%E5%85%83%E7%B4%A0-addeventlistener(-)%E6%96%B9%E6%B3%95%E6%9D%A5%E7%BB%91%E5%AE%9A%E4%BA%8B%E4%BB%B6-%EF%BC%88%E5%8F%AF%E7%BB%91%E5%AE%9A%E5%A4%9A%E4%B8%AA%EF%BC%8C%E4%BC%9A%E4%BE%9D%E6%AC%A1%E6%89%A7%E8%A1%8C%EF%BC%89}
<script>
// 通过元素 addEventListener( )方法来绑定事件
btn.addEventListener("click", function(){
alert("哈哈哈哈~")
})
</script>
>文档加载 {#%3E%E6%96%87%E6%A1%A3%E5%8A%A0%E8%BD%BD}
网页是自上向下加载的,如果将 js 的代码编写到网页的上边,在 js 代码执行时,网页还没有加载完毕,这时候会出现无法获取到 DOM 对象的情况。
如何解决这个问题?
将 script 标签编写到 body 的最后 {#%E5%B0%86-script-%E6%A0%87%E7%AD%BE%E7%BC%96%E5%86%99%E5%88%B0-body-%E7%9A%84%E6%9C%80%E5%90%8E}
<body>
\<button id="btn"\>点我一下\</button\>
\<script\>
const btn = document.getElementById("btn")
console.log(btn)
\</script\>
`</body>
`
将代码编写到 window.onload 的回调函数中 {#%E5%B0%86%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99%E5%88%B0-window.onload-%E7%9A%84%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E4%B8%AD}
<body>
\<button id="btn"\>点我一下\</button\>
\<script\>
// window.onload 事件会在窗口中的内容加载完毕之后才触发
window.onload = function () {
const btn = document.getElementById("btn")
console.log(btn)
}
window.addEventListener("load", function () {
const btn = document.getElementById("btn")
alert(btn)
})
\</script\>
`</body>`
将代码编写到 document 对象的 DOMContentLoaded 回调函数中 {#%E5%B0%86%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99%E5%88%B0-document-%E5%AF%B9%E8%B1%A1%E7%9A%84-domcontentloaded-%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E4%B8%AD}
<script>
// document的DOMContentLoaded事件会在当前文档加载完毕之后触发
document.addEventListener("DOMContentLoaded", function () {
const btn = document.getElementById("btn")
alert(btn)
})
</script>
将代码编写到外部 js 文件,以 defer 形式引入 {#%E5%B0%86%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99%E5%88%B0%E5%A4%96%E9%83%A8-js-%E6%96%87%E4%BB%B6%EF%BC%8C%E4%BB%A5-defer-%E5%BD%A2%E5%BC%8F%E5%BC%95%E5%85%A5}
<script defer src="./script/script.js"></script>
>Dom的修改 {#%3Edom%E7%9A%84%E4%BF%AE%E6%94%B9}
element.appendChild {#element.appendchild}
- 给一个节点添加子节点
<ul id="list">
<li id="swk">孙悟空</li>
<li id="zbj">猪八戒</li>
<li id="shs">沙和尚</li>
</ul>
\<button id="btn"\>按钮\</button\>
\<script\>
const list = document.getElementById("list")
// 获取按钮
const btn01 = document.getElementById("btn01")
btn01.onclick = function () {
const li = document.createElement("li")
// 向li中添加文本
li.textContent = "唐僧"
// 给li添加id属性
li.id = "ts"
`// 用于给一个节点添加子节点
list.appendChild(li)
</script>`
element.insertAdjacentElement(position, element) {#element.insertadjacentelement(position%2C-element)}
- 可以向元素的任意位置添加元素
参数:
beforeend 标签的最后
afterbegin 标签的开始
beforebegin 在元素的前边插入元素(兄弟元素)
afterend 在元素的后边插入元素(兄弟元素)
<script>
list.insertAdjacentElement("afterend", li)
</script>
element.insertAdjacentHTML(position, text) {#element.insertadjacenthtml(position%2C-text)}
- 将HTML字符串添加到任意位置
参数:
beforeend 标签的最后
afterbegin 标签的开始
beforebegin 在元素的前边插入元素(兄弟元素)
afterend 在元素的后边插入元素(兄弟元素)
<script>
list.insertAdjacentHTML("beforeend", "<li id='bgj'>白骨精</li>")
</script>
element.replaceWith {#element.replacewith}
- 使用一个元素替换当前元素
const bt = document.getElementById("btn")
btn02.onclick = function(){
// 创建一个蜘蛛精替换孙悟空
const li = document.createElement("li")
li.textContent = "蜘蛛精"
li.id = "zzj"
// 获取swk
const swk = document.getElementById("swk")
// replaceWith() 使用一个元素替换当前元素
swk.replaceWith(li)
`}`
element.remove {#element.remove}
- 用来删除一个元素
<script>
swk.remove()
</script>
>小练习 {#%3E%E5%B0%8F%E7%BB%83%E4%B9%A0}
>节点的复制 {#%3E%E8%8A%82%E7%82%B9%E7%9A%84%E5%A4%8D%E5%88%B6}
cloneNode() {#clonenode()}
- 对节点的所有属性进行复制(只会复制当前节点,而不会复制节点的子节点)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn01">点我一下</button>
<ul id="list1">
<li id="l1">孙悟空</li>
<li id="l2">猪八戒</li>
<li id="l3">沙和尚</li>
</ul>
<ul id="list2">
<li>蜘蛛精</li>
</ul>
<script>
/* 点击按钮后,将id为l1的元素添加list2中 */
const list2 = document.getElementById("list2")
const l1 = document.getElementById("l1")
const btn01 = document.getElementById("btn01")
btn01.onclick = function () {
// 用来对节点进行复制的,true作为参数,该方法将子节点一起复制
const newL1 = l1.cloneNode(true)
newL1.id = "newL1"
list2.appendChild(newL1)
}
</script>
</body>
`</html>`
>修改CSS样式 {#%3E%E4%BF%AE%E6%94%B9css%E6%A0%B7%E5%BC%8F}
元素.style.样式名 = 样式值 {#%E5%85%83%E7%B4%A0.style.%E6%A0%B7%E5%BC%8F%E5%90%8D-%3D-%E6%A0%B7%E5%BC%8F%E5%80%BC}
如果样式里含有-,则需要将样式表修改为驼峰命名法
background-color --> backgroundColor
<button id="btn">点我一下</button>
<div class="box1"></div>
<script>
const btn = document.getElementById("btn")
const box1 = document.querySelector(".box1")
btn.onclick = function () {
// 修改box1的样式
// 修改样式的方式:元素.style.样式名 = 样式值
// 如果样式名中含有-,则需要将样式表修改为驼峰命名法
// background-color --> backgroundColor
box1.style.width = "400px"
box1.style.height = "400px"
box1.style.backgroundColor = "yellow"
}
`</script>`
>读取CSS样式 {#%3E%E8%AF%BB%E5%8F%96css%E6%A0%B7%E5%BC%8F}
getComputedStyle() {#getcomputedstyle()}
- 返回一个包含当前元素所有的生效样式的对象
参数:
1.要获取样式的对象
2.要获取的伪元素
返回值:
返回的一个对象,对象中储存了当前元素的样式
<button id="btn">点我一下</button>
<div class="box1"></div>
<script>
/*
点击按钮后,读取元素的css样式
*/
const btn = document.getElementById("btn")
const box1 = document.querySelector(".box1")
btn.onclick = function () {
const styleObj = getComputedStyle(box1)
console.log(styleObj.width)
console.log(styleObj.left)
// console.log(parseInt(styleObj.width) + 100)
// box1.style.width = parseInt(styleObj.width) + 100 + "px"
// console.log(styleObj.backgroundColor)
const beforeStyle = getComputedStyle(box1, "::before")
// console.log(beforeStyle.color)
console.log(box1.firstElementChild)
}
`</script`
element.clientHeight / Width {#element.clientheight-%2F-width}
- 获取元素内部的宽度和高度(包括内容区和内边距)
element.offsetHeight / Width {#element.offsetheight-%2F-width}
- 获取元素的可见框大小(包括内容区、内边距和边框)
element.scrollHeight / Width {#element.scrollheight-%2F-width}
- 获取元素滚动区域的大小
element.offsetParent {#element.offsetparent}
- 获取元素的定位父元素
当前元素最近的开启了定位的祖先元素,如果定位的元素都没有开启定位,则返回 body。
element.offsetTop / Left {#element.offsettop-%2F-left}
- 获取元素相对于其定位父元素的偏移量
element.scrollTop / Left {#element.scrolltop-%2F-left}
- 获取或设置元素滚动条的偏移量
<button id="btn">点我一下</button>
<hr>
<div>
<div id="box1">
<div id="box2"></div>
</div>
</div>
<script>
const btn = document.getElementById("btn")
const box1 = document.getElementById("box1")
btn.onclick = function(){
// console.log(box1.scrollHeight)
// console.log(box1.offsetParent)
console.log(box1.scrollTop)
}</code></pre>
### \>修改操作Class {#%3E%E4%BF%AE%E6%94%B9%E6%93%8D%E4%BD%9Cclass}
#### element.classList {#element.classlist}
> 这是一个对象,并提供了对当前元素的类的各种操作方法
>
>
#### element.classList.add( ) 向元素添加一个或多个Class {#element.classlist.add(-)-%E5%90%91%E5%85%83%E7%B4%A0%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%88%96%E5%A4%9A%E4%B8%AAclass}
#### element.classList.remove( ) 移除元素中的一个或多个Class {#element.classlist.remove(-)-%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0%E4%B8%AD%E7%9A%84%E4%B8%80%E4%B8%AA%E6%88%96%E5%A4%9A%E4%B8%AAclass}
#### element.classList.toggle( ) 切换元素的Class {#element.classlist.toggle(-)-%E5%88%87%E6%8D%A2%E5%85%83%E7%B4%A0%E7%9A%84class}
#### element.classList.replace( ) 替换Class {#element.classlist.replace(-)-%E6%9B%BF%E6%8D%A2class}
#### element.classList.contains( ) 检查Class {#element.classlist.contains(-)-%E6%A3%80%E6%9F%A5class}
```vbscript-html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box1 {
width: 200px;
height: 200px;
background-color: #bfa;
}
.box2{
background-color: yellow;
width: 300px;
height: 500px;
border: 10px greenyellow solid;
}
</style>
</head>
<body>
<button id="btn">点我一下</button>
<hr />
<div class="box1 box3 box4"></div>
<script>
/*
点击按钮后,修改box1的宽度
*/
const btn = document.getElementById("btn")
const box1 = document.querySelector(".box1")
btn.onclick = function () {
// 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)
}
</script>
</body>
`</html>`
```
<br />
### \>事件对象 {#%3E%E4%BA%8B%E4%BB%B6%E5%AF%B9%E8%B1%A1}
>
> #### 简介(Event) {#%E7%AE%80%E4%BB%8B(event)}
>
>
>
>
>
> * 事件对象就是有浏览器在事件触发时所创建的对象,这个对象中封装了时间相关的各种信息。
>
>
>
>
> * 通过事件对象可以获取到事件的详细信息,比如:鼠标的坐标、键盘的按键...
>
>
>
>
> * 浏览器在创建时间后,会将事件对象作为响应函数的参数传递,所以我们可以在事件的回调函数中定义一个形参来接收事件对象。
>
>
>
>
> * 在DOM中存在着多种不同类型的事件对象,它们有着共同的祖先,就是 Event。
>
>
>
>
```vbscript-html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
#box1{
width: 300px;
height: 300px;
border: 10px greenyellow solid;
}
</style>
</head>
<body>
<div id="box1"></div>
<script>
const box1 = document.getElementById("box1")
// box1.onmousemove = event => {
// console.log(event)
// }
box1.addEventListener("mousemove", event => {
console.log(event.clientX, event.clientY)
box1.textContent = event.clientX + "," + event.clientY
})
</script>
</body>
`</html>`
```
<br />
### 冒泡(bubble) {#%E5%86%92%E6%B3%A1(bubble)}
> 事件的冒泡就是指事件的向上传导。
>
>
> 当元素上的某个事件被触发后,其祖先元素上的相同事件也会被同时触发。
>
>
> 冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在,所以我可以通过时间对象来取消冒泡
>
>
#### event.target 触发事件的对象 {#event.target-%E8%A7%A6%E5%8F%91%E4%BA%8B%E4%BB%B6%E7%9A%84%E5%AF%B9%E8%B1%A1}
#### event.currentTarget 绑定事件的对象(同this) {#event.currenttarget-%E7%BB%91%E5%AE%9A%E4%BA%8B%E4%BB%B6%E7%9A%84%E5%AF%B9%E8%B1%A1(%E5%90%8Cthis)}
#### event.stopPropagetion( ) 停止事件传导 {#event.stoppropagetion(-)-%E5%81%9C%E6%AD%A2%E4%BA%8B%E4%BB%B6%E4%BC%A0%E5%AF%BC}
#### event.preventDefault( ) 取消默认行为 {#event.preventdefault(-)-%E5%8F%96%E6%B6%88%E9%BB%98%E8%AE%A4%E8%A1%8C%E4%B8%BA}
````vbscript-html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
#box1 {
width: 300px;
height: 300px;
background-color: greenyellow;
}
#box2 {
width: 250px;
height: 250px;
background-color: #ff0;
}
#box3 {
width: 200px;
height: 200px;
background-color: orange;
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<a id="chao" href="https://parlo.cc">超链接</a>
<script>
// 事件对象
const box1 = document.getElementById("box1")
const box2 = document.getElementById("box2")
const box3 = document.getElementById("box3")
const chao = document.getElementById("chao")
chao.addEventListener("click", (event) => {
event.preventDefault() // 取消默认行为
alert("被点了~~~")
})
box1.addEventListener("click", function (event) {
// alert(event)
// console.log(event.target)
// console.log(this)
console.log(event.currentTarget)
// alert("Hello 我是box1")
})
// box2.addEventListener("click", function(event){
// event.stopPropagation()
// alert("我是box2")
// })</code></pre>
### \>事件委派 {#%3E%E4%BA%8B%E4%BB%B6%E5%A7%94%E6%B4%BE}
委派就是将本该绑定给多个元素的事件,统一绑定给 document,这样可以降低代码复杂度,方便维护。
```vbscript-html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn">点我一下</button>
<hr />
<ul id="list">
<li><a href="javascript:;">链接一</a></li>
<li><a href="javascript:;">链接二</a></li>
<li><a href="javascript:;">链接三</a></li>
<li><a href="javascript:;">链接四</a></li>
</ul>
<script>
/*
我一个希望:
只绑定一次事件,既可以让所有的超链接,包括当前的和未来新建的超链接都具有这些事件
思路:
可以将事件统一绑定给document,这样点击超链接时由于事件的冒泡,
会导致document上的点击事件被触发,这样只绑定一次,所有的超链接都会具有这些事件
*/
const list = document.getElementById("list")
const btn = document.getElementById("btn")
// 获取list中的所有链接
const links = list.getElementsByTagName("a")
document.addEventListener("click", (event) => {
// 在执行代码前,先来判断一下事件是由谁触发
// 检查event.target 是否在 links 中存在
// console.log(Array.from(links))
if([...links].includes(event.target)){
alert(event.target.textContent)
}
})
// 点击按钮后,在ul中添加一个新的li
btn.addEventListener("click", () => {
list.insertAdjacentHTML(
"beforeend",
"<li><a href='javascript:;'>新超链接</a></li>"
)
})
</script>
</body>
`</html>`
```
<br />
### \>事件的捕获 {#%3E%E4%BA%8B%E4%BB%B6%E7%9A%84%E6%8D%95%E8%8E%B7}
> 在 DOM 中,事件的传播主要分为三个阶段
>
>
>
>
> * 捕获阶段(从祖先元向目标元素进行事件的捕获)
>
>
>
>
> * 目标阶段(触发事件的对象)
>
>
>
>
> * 冒泡阶段(由目标元素向祖先元素进行事件的冒泡)
>
>
>
>
当前元素触发事件后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获。默认情况下,事件不会在捕获阶段触发。
如果希望在捕获阶段触发事件,可以将 addEventListener 的第三个参数设置为 true。一般情况下我们不希望事件在捕获阶段实现,所以通常我们并不需要设置第三个参数。
#### event.Phase 表示事件触发的阶段 {#event.phase-%E8%A1%A8%E7%A4%BA%E4%BA%8B%E4%BB%B6%E8%A7%A6%E5%8F%91%E7%9A%84%E9%98%B6%E6%AE%B5}
> 1 捕获阶段,2 目标阶段,3 冒泡阶段
>
>
```vbscript-html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
#box1 {
width: 300px;
height: 300px;
background-color: greenyellow;
}
#box2 {
width: 200px;
height: 200px;
background-color: orange;
}
#box3 {
width: 100px;
height: 100px;
background-color: tomato;
}
</style>
</head>
<body>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
<script>
const box1 = document.getElementById("box1")
const box2 = document.getElementById("box2")
const box3 = document.getElementById("box3")
box1.addEventListener("click", event => {
alert("1" + event.eventPhase)
})
box2.addEventListener("click", event => {
alert("2" + event.eventPhase)
})
box3.addEventListener("click", event => {
alert("3" + event.eventPhase)
})
</script>
</body>
`</html>`
```
<br />
<br />
````