JavaScript - DOM
编辑DOM,全称Document Object Model,中文翻译为文档对象模型。DOM属于Web API的一部分。Web API中定义了非常多的对象,通过这些对象可以完成对网页的各种操作(添加删除元素、发送请求、操作浏览器等)。
什么是DOM
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>
>概念
节点(Node)
在DOM标准下,网页中的每一个部分都会转换为对象。这些对象有一个共同的称呼——节点(Node)。一个页面将会由多个节点构成,虽然都称为节点,但是它们却有着不同的类型:
文档节点
元素节点
文本节点
属性节点
…
每一个节点都有其不同的作用,文档节点表示整个网页,元素节点表示某个标签,文本节点表示网页中的文本内容,属性节点表示标签中的各种属性。如果从对象的结构上来讲,这些对象都有一个共同的父类Node。总的来说,都是属于节点,但是具体类型不同。
关系
祖先 —— 包含后代元素的元素是祖先元素
后代 —— 被祖先元素包含的元素是后代元素
父 —— 直接包含子元素的元素是父元素
子 —— 直接被父元素包含的元素是子元素
兄弟 —— 拥有相同父元素的元素是兄弟元素
小试牛刀
要使用DOM来操作网页,我们需要浏览器至少得先给我一个对象,才能去完成各种操作。所以浏览器已经为我们提供了一个document对象,它是一个全局变量可以直接使用,document代表的是整个的网页。
<button id="btn">点我一下</button>
<script>
// 获取btn对象
const btn = document.getElementById("btn")
// 修改btn中的文字
btn.innerText = "Click ME"
</script>
>文档节点(document)
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)
在网页中,每一个标签都是一个元素节点
如何获取元素节点对象?
通过document对象来获取元素节点
通过document对象来创建元素节点
通过document来获取已有的元素节点:
document.getElementById()
根据id获取一个元素节点对象
<button id="btn">点我一下</button>
<script>
const btn = document.getElementById("btn")
console.log(btn)
</script>
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()
根据标签名获取一组元素节点对象
<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()
根据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()
根据选择器去页面中查询元素
<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()
根据选择器去页面中查询第一个符合条件的元素
<div>我是div</div>
<div>我是div</div>
<script>
// 会返回一个类数组(不会实时更新)
const div = document.querySelector("div") // .xx | #xx
console.log(divs2) // NodeList(2) [input,input]
</script>
创建一个元素节点
document.createElement()
根据标签名创建一个元素节点对象
const h2 = document.createElement("h2") // 创建了H2,但未添加
>元素的属性和方法
<div id="box1">我是box1 </div>
<script>
const box1 = document.getElementById("box1")
cosole.log(box1.__proto__)
</script>
div的原型链
HTMLDivElement -> HTMLElement -> Element -> Node -> ...
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
获取当前元素的子元素
<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
获取当前元素的第一个子元素
<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
获取当前元素的最后一个子元素
console.log(box1.lastElementChild) //输出: <span class="s1">我是s1-2</span>
element.nextElementSibling
获取当前元素的下一个兄弟元素
console.log(box1.nextElementSibling) //输出: <h1>hello,world</h1>
element.previousElementSibling
获取当前元素的前一个兄弟元素
console.log(box1.previousElementSibling) //输出: <p>Hello world! This is HTML5 Boilerplate.</p>
element.parentNode
获取当前元素的父节点
console.log(box1.parentNode) //输出: <body>...</body>
element.tagName
获取当前元素的标签名
console.log(box1.tagName) //输出: DIV
>文本节点(Text)
在DOM中,网页中所有的文本内容都是文本节点对象。
可以通过元素来获取其中的文本节点对象,但是我们通常不会这么做。
我们可以直接通过元素去修改其中的文本
修改文本的三个属性
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
获取或修改元素中的文本内容
<script>
// innerText获取内容时,会考虑css样式
// 通过innerText去读取CSS样式,会触发网页的重排(计算CSS样式)
const box1 = document.getElementById("box1")
box1.innerText = "新的内容"
// 当字符串中有标签时,会自动对标签进行转义
box1.innerText = "<li>我是li</>" // <li> --> <li>
</script>
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)
在DOM也是一个对象,通常不需要获取对象而是直接通过元素即可完成对其的各种操作。
如何操作属性节点:
方式一
读取:元素.属性名(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>
方式二
读取:元素.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)
事件就是用户和页面之间发生的交互行为。例如:点击按钮、鼠标移动、双击按钮、敲击键盘、松开按键....
可以通过为时间绑定响应函数(回调函数),来完成和用户之间的交互。
绑定响应函数
直接在元素的属性中设置
<button id="btn" onclick="alert('你点我干嘛~')">点我一下</button>
为元素指定属性设置(回调函数)来绑定事件 (一个事件只能绑定一个响应函数)
<script>
// 获取到按钮对象
const btn = document.getElementById("btn")
// 为按钮对象的事件属性设置响应函数
btn.onclick = function(){
alert("你点我干嘛~")
}
</script>
通过元素 addEventListener( )方法来绑定事件 (可绑定多个,会依次执行)
<script>
// 通过元素 addEventListener( )方法来绑定事件
btn.addEventListener("click", function(){
alert("哈哈哈哈~")
})
</script>
>文档加载
网页是自上向下加载的,如果将 js 的代码编写到网页的上边,在 js 代码执行时,网页还没有加载完毕,这时候会出现无法获取到 DOM 对象的情况。
如何解决这个问题?
将 script 标签编写到 body 的最后
<body>
<button id="btn">点我一下</button>
<script>
const btn = document.getElementById("btn")
console.log(btn)
</script>
</body>
将代码编写到 window.onload 的回调函数中
<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 回调函数中
<script>
// document的DOMContentLoaded事件会在当前文档加载完毕之后触发
document.addEventListener("DOMContentLoaded", function () {
const btn = document.getElementById("btn")
alert(btn)
})
</script>
将代码编写到外部 js 文件,以 defer 形式引入
<script defer src="./script/script.js"></script>
>Dom的修改
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)
可以向元素的任意位置添加元素
参数:
beforeend 标签的最后
afterbegin 标签的开始
beforebegin 在元素的前边插入元素(兄弟元素)
afterend 在元素的后边插入元素(兄弟元素)
<script>
list.insertAdjacentElement("afterend", li)
</script>
element.insertAdjacentHTML(position, text)
将HTML字符串添加到任意位置
参数:
beforeend 标签的最后
afterbegin 标签的开始
beforebegin 在元素的前边插入元素(兄弟元素)
afterend 在元素的后边插入元素(兄弟元素)
<script>
list.insertAdjacentHTML("beforeend", "<li id='bgj'>白骨精</li>")
</script>
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
用来删除一个元素
<script>
swk.remove()
</script>
>小练习
>节点的复制
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样式
元素.style.样式名 = 样式值
如果样式里含有-,则需要将样式表修改为驼峰命名法
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样式
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.offsetHeight / Width
获取元素的可见框大小(包括内容区、内边距和边框)
element.scrollHeight / Width
获取元素滚动区域的大小
element.offsetParent
获取元素的定位父元素
当前元素最近的开启了定位的祖先元素,如果定位的元素都没有开启定位,则返回 body。
element.offsetTop / Left
获取元素相对于其定位父元素的偏移量
element.scrollTop / 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)
}
>修改操作Class
element.classList
这是一个对象,并提供了对当前元素的类的各种操作方法
element.classList.add( ) 向元素添加一个或多个Class
element.classList.remove( ) 移除元素中的一个或多个Class
element.classList.toggle( ) 切换元素的Class
element.classList.replace( ) 替换Class
element.classList.contains( ) 检查Class
<!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>
>事件对象
简介(Event)
事件对象就是有浏览器在事件触发时所创建的对象,这个对象中封装了时间相关的各种信息。
通过事件对象可以获取到事件的详细信息,比如:鼠标的坐标、键盘的按键...
浏览器在创建时间后,会将事件对象作为响应函数的参数传递,所以我们可以在事件的回调函数中定义一个形参来接收事件对象。
在DOM中存在着多种不同类型的事件对象,它们有着共同的祖先,就是 Event。
<!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>
冒泡(bubble)
事件的冒泡就是指事件的向上传导。
当元素上的某个事件被触发后,其祖先元素上的相同事件也会被同时触发。
冒泡的存在大大的简化了代码的编写,但是在一些场景下我们并不希望冒泡存在,所以我可以通过时间对象来取消冒泡
event.target 触发事件的对象
event.currentTarget 绑定事件的对象(同this)
event.stopPropagetion( ) 停止事件传导
event.preventDefault( ) 取消默认行为
<!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")
// })
>事件委派
委派就是将本该绑定给多个元素的事件,统一绑定给 document,这样可以降低代码复杂度,方便维护。
<!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>
>事件的捕获
在 DOM 中,事件的传播主要分为三个阶段
捕获阶段(从祖先元向目标元素进行事件的捕获)
目标阶段(触发事件的对象)
冒泡阶段(由目标元素向祖先元素进行事件的冒泡)
当前元素触发事件后,会先从当前元素最大的祖先元素开始向当前元素进行事件的捕获。默认情况下,事件不会在捕获阶段触发。
如果希望在捕获阶段触发事件,可以将 addEventListener 的第三个参数设置为 true。一般情况下我们不希望事件在捕获阶段实现,所以通常我们并不需要设置第三个参数。
event.Phase 表示事件触发的阶段
1 捕获阶段,2 目标阶段,3 冒泡阶段
<!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>
- 3
- 0
-
分享