目录
一、简介
DOM,全称Document Object Model,中文翻译为文档对象模型。DOM属于Web API的一部分。Web API中定义了非常多的对象,通过这些对象可以完成对网页的各种操作(添加删除元素、发送请求、操作浏览器等)。
DOM中的D意为Document,即文档。所谓文档就是指整个网页,换言之,DOM是用来操作网页的。
O意为Object,即对象。DOM将网页中的每一部分内容都转换为了对象。转换对象以后,我们就可以以面向对象的方式去操作网页,想要操作哪个元素就获取哪个元素的对象,然后通过调用其方法或属性完成各种操作。
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>
二、节点Node
在DOM标准下,网页中的每一个部分都会转换为对象。这些对象有一个共同的称呼——节点(Node)。一个页面将会由多个节点构成,虽然都称为节点,但是它们却有着不同的类型:
- 文档节点
- 元素节点
- 文本节点
- 属性节点
- …
每一个节点都有其不同的作用,文档节点表示整个网页,元素节点表示某个标签,文本节点表示网页中的文本内容,属性节点表示标签中的各种属性。如果从对象的结构上来讲,这些对象都有一个共同的父类Node。总的来说,都是属于节点,但是具体类型不同。
关系:
- 祖先 —— 包含后代元素的元素是祖先元素
- 后代 —— 被祖先元素包含的元素是后代元素
- 父 —— 直接包含子元素的元素是父元素
- 子 —— 直接被父元素包含的元素是子元素
- 兄弟 —— 拥有相同父元素的元素是兄弟元素
三、document
1、简介
要使用DOM来操作网页,我们需要浏览器至少得先给我一个对象,才能去完成各种操作。所以浏览器已经为我们提供了一个document对象,它是一个全局变量可以直接使用。
document代表的是整个的网页。比document大的是window。
2、document对象的原型链
HTMLDocument -> Document -> Node -> EventTarget -> Object.prototype -> null。
凡是在原型链上存在的对象的属性和方法都可以通过Document去调用。
3、部分属性
document.documentElement --> html根元素
document.head --> head元素
document.title --> title元素
document.body --> body元素
document.linksimgs/forms... --> 获取页面中所有的超链接/图片/表单...
四、元素节点
在网页中,每一个标签都是一个元素节点
1、如何获取元素节点对象
通过document对象来获取已存在的元素节点
document.getElementById():根据id获取一个元素节点对象
document.getElementsByClassName():根据元素的class属性值获取一组元素节点对象,返回的是一个类数组对象。该方法返回的结果是一个实时更新的集合,当网页中新添加元素时,集合也会实时的刷新。
document.getElementsByTagName():根据标签名获取一组元素节点对象。返回的结果是可以实时更新的集合。document.getElementsByTagName("*") 获取页面中所有的元素。
document.getElementsByName():根据name属性获取一组元素节点对象。返回一个实时更新的集合。主要用于表单项
document.querySelectorAll():根据选择器去页面中查询元素。会返回一个类数组(不会实时更新)。
document.querySelector():根据选择器去页面中查询第一个符合条件的元素。
通过document对象来创建元素节点
document.createElement():根据标签名创建一个元素节点对象。
2、原型链
<!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>
<div id="box1">
</div>
</body>
</html>
以上代码中div元素的原型链:HTMLDivElement(因元素的不同而不同) -> HTMLElement -> Element -> Node -> (与document一致) EventTarget -> Object.prototype -> null。
3、通过元素节点对象获取其他节点的方法
一些与document重复的方法:element.getElementById()、element.getElementsByClassName()、element.getElementsByTagName()
element.childNodes 获取当前元素的子节点(会包含空白的子节点)
element.children 获取当前元素的子元素
element.firstElementChild 获取当前元素的第一个子元素
element.lastElementChild 获取当前元素的最后一个子元素
element.nextElementSibling 获取当前元素的下一个兄弟元素
element.previousElementSibling 获取当前元素的前一个兄弟元素
element.parentNode 获取当前元素的父节点
element.tagName 获取当前元素的标签名
五、文本节点
在DOM中,网页中所有的文本内容都是文本节点对象, 可以通过元素来获取其中的文本节点对象,但是我们通常不会这么做。我们可以直接通过元素去修改其中的文本
修改文本的三个属性
element.textContent 获取或修改元素中的文本内容
- 获取的是标签中的内容,不会考虑css样式
element.innerText 获取或修改元素中的文本内容
- innerText获取内容时,会考虑css样式
- 通过innerText去读取CSS样式,会触发网页的重排(计算CSS样式)
- 当字符串中有标签时,会自动对标签进行转义,将标签作为文本
- <li> --> <li>
element.innerHTML 获取或修改元素中的html代码
- 可以直接向元素中添加html代码
- innerHTML插入内容时,有被xss注入的风险
<div id="box1">
<span>我是box1</span>
</div>
六、属性节点
在DOM也是一个对象,通常不需要获取对象而是直接通过元素即可完成对其的各种操作
操作属性节点
方式一:
读取:元素.属性名(注意,class属性需要使用className来读取)。读取一个布尔值时,会返回true或false,如disabled属性
<input class="a" type="text" name="username" value="admin">
const input = document.getElementsByName("username")[0]
const input = document.querySelector("[name=username]")
修改:元素.属性名 = 属性值
方式二:
读取:元素.getAttribute(属性名)
修改:元素.setAttribute(属性名, 属性值)
删除:元素.removeAttribute(属性名)
七、事件
事件就是用户和页面之间发生的交互行为,比如:点击按钮、鼠标移动、双击按钮、敲击键盘、松开按键...
可以通过为事件绑定响应函数(回调函数),来完成和用户之间的交互。
绑定响应函数的方式:
1.可以直接在元素的属性中设置
<!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" onmouseenter="alert('你点我干嘛~')">点我一下</button>
</script>
</body>
</html>
2.可以通过为元素的指定属性设置回调函数的形式来绑定事件(一个事件只能绑定一个响应函数)
<!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>
<script>
// 获取到按钮对象
const btn = document.getElementById("btn")
// 为按钮对象的事件属性设置响应函数
btn.onclick = function(){
alert("我又被点了一下~~")
}
</script>
</body>
</html>
3.可以通过元素addEventListener()方法来绑定事件
<!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>
<script>
// 获取到按钮对象
const btn = document.getElementById("btn")
// 给对象绑定监听事件
btn.addEventListener("click", function(){
alert("哈哈哈")
})
</script>
</body>
</html>
八、文档加载
网页是自上向下加载的,如果将js代码编写到网页的上边,js代码在执行时,网页还没有加载完毕,这时会出现无法获取到DOM对象的情况。
解决办法:
1、将script标签编写到body的最后
如果script标签鞋到了body外面,也会自动把它放进body里面
<!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>
<script>
const btn = document.getElementById("btn")
console.log(btn)
</script>
</body>
</html>
2、将代码编写到window.onload的回调函数中
window.onload 事件会在窗口中的内容加载完毕之后才触发。窗口不止一个文档。
<!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>
<script>
window.onload = function () {
const btn = document.getElementById("btn")
console.log(btn)
}
</script>
</head>
<body>
<button id="btn">点我一下</button>
</body>
</html>
3、 将代码编写到document对象的DOMContentLoaded的回调函数中(执行时机更早)
document的DOMContentLoaded事件会在当前文档加载完毕之后触发
<!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>
<script>
document.addEventListener("DOMContentLoaded", function () {
const btn = document.getElementById("btn")
alert(btn)
})
</script>
</head>
<body>
<button id="btn">点我一下</button>
</body>
</html>
4、将代码编写到外部的js文件中,然后以defer的形式进行引入(执行时机更早,早于DOMContentLoaded)
<!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>
<script defer src="./script/script.js"></script>
</head>
<body>
</body>
</html>
5、将代码编写到外部的js文件中,然后以async的形式进行引入(执行时机更早,早于DOMContentLoaded)
<!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>
<script async src="./script/script.js"></script>
</head>
<body>
</body>
</html>
九、修改DOM
1、appendChild() :用于给一个节点添加子节点
2、insertAdjacentElement(要添加的位置,要添加的元素):可以向元素的任意位置添加元素
要添加的位置=beforeend 标签的最后 ;afterbegin 标签的开始 ;beforebegin 在元素的前边插入元素(兄弟元素); afterend 在元素的后边插入元素(兄弟元素)
list.insertAdjacentHTML("beforeend", "<li id='bgj'>白骨精</li>")
3、元素1.replaceWith(元素2) :使用一个元素替换当前元素
// 获取swk
const swk = document.getElementById("swk")
swk.replaceWith(li)
4、元素.remove():方法用来删除当前元素
const swk = document.getElementById("swk")
swk.remove()
十、节点的复制
节点.cloneNode()
使用 cloneNode() 方法对节点进行复制时,它会复制节点的所有特点包括各种属性。这个方法默认只会复制当前节点,而不会复制节点的子节点。可以传递一个true作为参数,这样该方法也会将元素的子节点一起复制。
十一、修改元素的css样式
元素.style.样式名 = 样式值
如果样式名中含有-,则需要将样式表修改为驼峰命名法。如果样式中有!important,再用上面的语法修改就改不了了
<!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;
}
</style>
</head>
<body>
<button id="btn">点我一下</button>
<hr />
<div class="box1"></div>
<script>
// 点击按钮后,修改box1的宽度
const btn = document.getElementById("btn")
const box1 = document.querySelector(".box1")
btn.onclick = function () {
box1.style.width = "400px"
box1.style.height = "400px"
box1.style.backgroundColor = "yellow" // 如果样式名中含有-,则需要将样式表修改为驼峰命名法
}
</script>
</body>
</html>
十二、读取元素的css样式(通用)
getComputedStyle(要获取样式的对象,要获取的伪元素):它会返回一个对象,这个对象中包含了当前元素所有的生效的样式。
<!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 {
height: 200px;
background-color: #bfa;
}
.box1::before {
content: "hello";
color: red;
}
</style>
</head>
<body>
<button id="btn">点我一下</button>
<hr />
<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)
const beforeStyle = getComputedStyle(box1, "::before") // 获取伪元素的样式
console.log(box1.firstElementChild)
}
</script>
</body>
</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 {
height: 200px;
background-color: #bfa;
}
.box1::before {
content: "hello";
color: red;
}
</style>
</head>
<body>
<button id="btn">点我一下</button>
<hr />
<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" // 使用、设置时要加上单位
}
</script>
</body>
</html>
十三、通过属性读取元素的css样式
元素.clientHeight
元素.clientWidth
- 获取元素内部的宽度和高度(包括内容区和内边距)
元素.offsetHeight
元素.offsetWidth
- 获取元素的可见框的大小(包括内容区、内边距和边框)
元素.scrollHeight
元素.scrollWidth
- 获取元素滚动区域的大小
元素.offsetParent
- 获取元素的定位父元素
- 定位父元素:离当前元素最近的开启了定位的祖先元素,
如果所有的元素都没有开启定位则返回body
元素.offsetTop
元素.offsetLeft
- 获取元素相对于其定位父元素的偏移量
元素.scrollTop
元素.scrollLeft
- 获取或设置元素滚动条的偏移量
十四、修改class间接修改css样式
通过class修改样式的好处:
1. 可以一次性修改多个样式
2. 对JS和CSS进行解耦
<!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"
}
</script>
</body>
</html>
元素.classList 是一个对象,对象中提供了对当前元素的类的各种操作方法
元素.classList.add() 向元素中添加一个或多个class
元素.classList.remove() 移除元素中的一个或多个class
元素.classList.toggle() 切换元素中的class
元素.classList.replace() 替换class
元素.classList.contains() 检查class
十五、事件对象
事件对象是有浏览器在事件触发时所创建的对象,这个对象中封装了事件相关的各种信息。通过事件对象可以获取到事件的详细信息,比如:鼠标的坐标、键盘的按键...浏览器在创建事件对象后,会将事件对象作为响应函数的参数传递,所以我们可以在事件的回调函数中定义一个形参来接收事件对象。
十六、Event对象
在DOM中存在着多种不同类型的事件对象,多种事件对象有一个共同的祖先 Event。
event.target :触发事件的对象
event.currentTarget :绑定事件的对象(同this)
event.stopPropagation() :停止事件的传导
event.preventDefault() :取消默认行为
十七、事件冒泡
事件的冒泡就是指事件的向上传到,当元素上的某个事件被触发后,其祖先元素上的相同事件也会同时被触发,冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在。不希望事件冒泡时,可以通过事件对象来取消冒泡
事件的冒泡和元素的样式无关,只和结构相关
十八、事件委派
希望只绑定一次事件,既可以让所有的超链接,包括当前的和未来新建的超链接都具有这些事件。
思路:可以将事件统一绑定给document,这样点击超链接时由于事件的冒泡, 会导致document上的点击事件被触发,这样只绑定一次,所有的超链接都会具有这些事件。
委派就是将本该绑定给多个元素的事件,统一绑定给document,这样可以降低代码复杂度方便维护。
十九、事件的传播机制
在DOM中,事件的传播可以分为三个阶段:
1.捕获阶段 (由祖先元素向目标元素进行事件的捕获)(默认情况下,事件不会在捕获阶段触发)
2.目标阶段 (触发事件的对象)
3.冒泡阶段 (由目标元素向祖先元素进行事件的冒泡)
二十、事件的捕获
指事件从外向内的传导,当前元素触发事件以后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获。
如果希望在捕获阶段触发事件,可以将addEventListener的第三个参数设置为true。一般情况下我们不希望事件在捕获阶段触发,所有通常都不需要设置第三个参数。