十. DOM和BOM
1. HelloWorld
-
要使用DOM来操作网页, 我们需要浏览器至少得先给我一个对象才能去完成各种操作
-
所以浏览器已经为我们提供了一个document对象, 它是一个全局变量可以直接使用
-
Document代表的是整个网页
let btnEl = document.getElementById("btn")
// 修改btn中的文字
btnEl.onclick = function () {
btnEl.textContent = "Hello World"
}
// btnEl.innerText = "Click Me"
2. 文档节点
- Document
-
document对象表示的是整个网页
-
document对象的原型链
- HTMLDocument – Document – Node – EventTarget – Object.propotype – null
-
凡是在原型链上存在的对象的属性和方法都可以通过Document调用
-
部分属性
- document.document.Element – html根元素
- document.head – head元素
- document.title – title元素
- document.body – body元素
- document.links – 获取页面中所有的超链接
-
3. 获取元素节点
<button id="btn01">点我一下</button>
<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>
<div>我是div</div>
<div>我是div</div>
<div>我是div</div>
<div>我是div</div>
<div>我是div</div>
<form>
<input type="text" name="username">
<input type="radio" name="gender" value="male"> 男
<input type="radio" name="gender" value="female"> 女
</form>
- 元素节点对象(Element)
- 在网页中, 每一个标签元素都是元素节点
- 如何获取元素节点对象?
- 通过document对象来获取节点
- 通过document对象来创建元素节点
- 通过document来获取已有的元素节点
-
document.getElementById()
- 根据id获取一个元素节点对象
-
document.getElementsClassName()
- 根据元素的class属性来获取一组元素节点对象
- 返回的是类数组对象
- 该方法返回的结果是一个实时更新的集合
- 当网页当中新添加元素时, 集合也会事实的刷新
-
document.getElementsByTagName()
- 根据标签名获取一组元素节点对象
- 返回的结果是可以实时更新的集合
- document.getElementsByTagName(“*”) 获取页面所有的元素
-
document.getElementsByName()
- 根据name属性获取一组元素节点对象
- 返回一个实时更新的集合
- 主要用于表单项
-
document.querySelectorAll()
- 根据选择器去页面中查询
- 它会返回一个类数组不会实时刷新
- 有一个没有都会返回伪数组
-
document.quserySelector()
- 根据选择器去页面中查询第一个符合条件的元素
- 它会返回一个类数组不会实时刷新
-
创建一个元素节点
- document.createElement()
- 根据标签名创建一个元素选择器
- document.createElement()
-
const btnEl = document.getElementById("btn01")
const s1El = document.getElementsByClassName("s1")
const divEl = document.getElementsByTagName("div")
const genderInput = document.getElementsByName("gender")
const divEls = document.querySelectorAll("div")
const div = document.querySelector("div")
const h2 = document.createElement("h2")
// for (let i = 0; i < s1El.length; i++) {
// alert(s1El[i])
// s1El[i].innerText = "你在干嘛"
// }
// console.log(document.getElementsByClassName("s1"))
// console.log(s1El)
// console.log(btnEl)
4. 元素属性和方法
-
div元素的原型链 span就是HMLSpanElement
- HTMLDivElement – HTMLElement – Element – Node – EventTarget – Object.propotype – null
-
通过元素节点对象获取其他节点的方法 带**的更重要
-
element.childNodes 获取当前元素的子节点 (会包括空白的子节点)
-
element.children 获取当前元素的子元素 **
-
element.firstChild 获取第一个子节点
-
element.firstElementChild 获取第一个子元素 **
-
element.lastChild 获取最后一个子节点
-
element.lastElementChild 获取最后一个子元素 **
-
element.nextSibling 获取后兄弟节点
-
element.nextElementSibling 获取后兄弟元素 **
-
element.preiousSibling 获取前兄弟节点
-
element.previousElementSibling 获取前兄弟元素 **
-
elenent.parentNode 获取父节点
-
element.parentElement 获取父元素
-
element.tagName 获取当前元素标签名
-
const box1 = document.querySelector("#box1")
// 只找box1里面的span
// const spans = box1.getElementsByTagName("span")
const spans = box1.getElementsByClassName("s1")
const cns = box1.childNodes
const children = box1.children
const last = box1.lastChild
console.log(last)
5. 文本节点
- 在DOM中, 网页中所有的文本内容都是文本节点对象
-
可以通过元素来获取其中的文本节点对象, 但是我们通常不会这么做
-
我们可以直接通过元素去修改其中的文本
- 修改文本的三个属性
- element.textContent 获取或修改元素中的文本内容
- 获取的是标签中的内容, 不会考虑css样式
- 当字符串中有特殊字符时, 会自动对标签进行转义
- 修改文本的三个属性
-
<li> <li>
-
element.innerText 获取或修改元素中的文本内容
- innerText会考虑css样式
- 通过innerText去读取css样式, 会触发网页的重排(计算css样式)
-
element.innerHTML 获取或修改元素中的HTML代码
- 可以直接向元素中添加html代码
- innerHTML在插入内容时, 有被xss注入的风险
const box1 = document.getElementById("box1")
// const text = box1.firstChild 不用这样
// console.log(text)
// box1.innerText = "xxx"
// box1.textContent = "你好易语言"
// box1.innerText = "你好text"
6. 属性节点
- 属性节点(Attr)
- 在DOM也是一个对象, 通常不需要获取对象而是直接通过元素即可完成对齐的各种操作
- 如何操作属性节点:
- 方式一:
- 读取: 元素.属性名 (注意class属性需要用className改)
- 读取一个布尔值时, 会返回一个true或者false
- 修改: 元素.属性名 = 属性值
- 方式二:
- 读取: 元素.getAttribute(属性名)
- 修改: 元素.setAttribute(属性名, 属性值)
- 删除: 元素.removeAttribute(属性名)
- 方式一:
// const input = document.getElementsByName("username")[0]
const input = document.querySelector("[name=username]")
// 方式一
// 修改
// input.value = "熏悟空"
// input.disabled = true
// input.disabled = false
// 读取class
// console.log(input.className)
// 方式二
// 修改
input.setAttribute("value", "猪八戒")
input.setAttribute("disabled", "disabled")
// 读取
console.log(input.getAttribute("type"))
console.log(input.getAttribute("name"))
7. 事件
- onclick 单击
- ondblclick 双击
- onmouseenter 鼠标移入
- 事件(Event)
- 事件就是用户和页面之间发生的交互行为
- 比如: 点击按钮, 鼠标移动, 双击按钮, 敲击键盘, 松开按键
- 可以通过为事件绑定响应函数(回调函数), 来完成和用户之间的交互
- 绑定相应函数的方式有多种
- 可以直接在元素的属性中设置
- 可以通过为元素的指定属性设置回调函数的形式来绑定事件
- 一个事件只能绑定一个响应函数
- 可以通过元素的addEventListener()方法来绑定事件
- 一个事件可以绑定多个响应函数
// 1. 获取到按钮对象
const btnEl = document.getElementById("btn")
// 2. 为按钮对象的事件属性设置响应函数
// btnEl.onclick = function() {
// console.log("点我干嘛")
// }
btnEl.addEventListener("click", function() {
console.log("你点我干嘛")
})
btnEl.addEventListener("click", function() {
console.log("你在惦记我")
})
8. 文档加载事件
- 网页是自上而下加载的, 如果将js代码编写在网页上边
-
js代码在执行时, 网页还没有加载完毕, 这时会出现无法获取到DOM对象的情况
-
window.onload 事件会在窗口内容加载完毕之后才触发
-
DOMContentLoaded事件会在当前文档加载完毕之后触发
-
如何解决这个问题
- 将script标签编写到body的最后 (*****)
- 将代码编写到onload回调函数中
- 可以当代码编写到document对象的DOMContentLoaded (执行时机更早)
- 将代码编写代外部js文件中, 然后以defer的进行引入 (执行时机更早, 早于DOMContentLoaded) (*****)
-
window.onload = function () {
const btn = document.getElementById("btn")
console.log(btn)
}
document.addEventListener("DOMContentLoaded", function() {
const btn = document.querySelector("#btn")
console.log(btn)
})
const btn = document.getElementById("btn")
console.log(btn)
9. 练习
.outer {
width: 604px;
margin: 50px auto;
/* 文本居中 按钮居中 */
text-align: center;
}
<div class="outer">
<p id="info">
总共n张图片, 当前是第n张
</p>
<div class="img-wrapper">
<img src="./images/banner_01.jpeg" alt="">
</div>
<div class="btn-wrapper">
<button id="prev">上一张</button>
<button id="next">下一张</button>
</div>
</div>
// 获取到图片
const img = document.getElementsByTagName("img")[0]
// 获取到按钮
const prev = document.getElementById("prev")
const next = document.getElementById("next")
// 获取info文字
let info = document.getElementById("info")
// 创建一个数组存储图片的路径
const imgArr = [
"./images/banner_01.jpeg",
"./images/banner_02.jpeg",
"./images/banner_03.jpeg",
"./images/banner_04.jpeg",
"./images/banner_05.jpeg"
]
// 创建一个变量来记录当前图片的索引
let current = 0
info.textContent = `总共 ${imgArr.length} 张图片, 当前是第${current + 1}张`
// 点击prev按钮后, 上一张
prev.onclick = function () {
current--
// 检查current是否合法
if (current < 0) {
current = imgArr.length - 1
}
img.src = imgArr[current]
info.textContent = `总共 ${imgArr.length} 张图片, 当前是第${current + 1}张`
}
// 点击next按钮后, 切换图片
next.onclick = function () {
// 切换图片
current++
// 判断是否合法
if (current > imgArr.length - 1) {
current = 0
}
img.src = imgArr[current]
info.textContent = `总共 ${imgArr.length} 张图片, 当前是第${current + 1}张`
}
10. 练习
<div>
<form action="#">
<div>
请选择你的爱好
<input type="checkbox" id="check-all"> <span class="qx">全选</span>
</div>
<div>
<input type="checkbox" name="hobby" value="乒乓球"> 乒乓球
<input type="checkbox" name="hobby" value="羽毛球"> 羽毛球
<input type="checkbox" name="hobby" value="篮球"> 篮球
<input type="checkbox" name="hobby" value="足球"> 足球
</div>
<div>
<button type="button" id="all">全选</button>
<button type="button" id="no">取消</button>
<button type="button" id="reverse">反选</button>
<button type="button" id="send">提交</button>
</div>
</form>
</div>
/*
全选功能
取消
反选
提交
让四个小checkbox和大的checkbox同步
*/
/*
全选
- 点击按钮, 使四个多选框都变成选中的状态
*/
// 获取all全选按钮
let allBtn = document.getElementById("all")
// 获取四个多选框
let hobbys = document.getElementsByName("hobby")
// 获取取消按钮
let noBtn = document.getElementById("no")
let qx = document.querySelector(".qx")
let checkAll = document.getElementById("check-all")
// 为全选按钮绑定响应函数
allBtn.onclick = function () {
// 将多选框设置为选中状态
for (let i = 0; i < hobbys.length; i++) {
hobbys[i].checked = true
qx.textContent = "取消"
}
checkAll.checked = true
}
// 取消功能, 点击取消, 取消所有的选择
noBtn.onclick = function () {
for (let i = 0; i < hobbys.length; i++) {
hobbys[i].checked = false
qx.textContent = "全选"
}
checkAll.checked = false
}
// 反选功能
let reverse = document.getElementById("reverse")
reverse.onclick = function () {
for (let i = 0; i < hobbys.length; i++) {
hobbys[i].checked = !hobbys[i].checked
if (hobbys[i].checked === true) {
qx.textContent = "取消"
checkAll.checked = true
}
if (hobbys[i].checked === false) {
qx.textContent = "全选"
checkAll.checked = false
}
}
}
// 提交按钮
// 点击按钮后, 将选中的按钮显示出来
let send = document.getElementById("send")
send.onclick = function () {
for (let i = 0; i < hobbys.length; i++) {
if (hobbys[i].checked) {
alert(hobbys[i].value)
hobbys[i].checked = false
checkAll.checked = false
}
}
}
// 全选按钮
// change 发生变化
checkAll.onclick = function () {
// 当事件的响应函数中, 响应函数绑定给谁this就是谁 (箭头函数除外)
// console.log(this)
for (let i = 0; i < hobbys.length; i++) {
// hobbys[i].checked = !hobbys[i].checked
hobbys[i].checked = this.checked
if (hobbys[i].checked === true) {
qx.textContent = "取消"
}
if (hobbys[i].checked === false) {
qx.textContent = "全选"
}
}
}
/*
使全选的checkAll和四个checked进行同步
如果四个全选, 则全选的checkAll也选中
如果四个没全选, 则全选的checkAll不选中
*/
for (let i = 0; i < hobbys.length; i++) {
hobbys[i].onchange = function() {
// 判断hbs是否为全选装填
// 获取所有选中的checkAll
// 我要找这堆找到选中的
let checkedBox = document.querySelectorAll("[name=hobby]:checked")
if(checkedBox.length === hobbys.length) {
checkAll.checked = true
qx.textContent = "取消"
}
if(checkedBox.length !== hobbys.length) {
checkAll.checked = false
qx.textContent = "全选"
}
}
}
10. DOM修改
<button id="btn01">按钮1</button>
<button id="btn02">按钮2</button>
<hr>
<ul id="list">
<li id="swk">孙悟空</li>
<li id="zbj">猪八戒</li>
<li id="shs">沙和尚</li>
</ul>
- 添加子节点 appendChild用于给一个节点添加一个子节点
-
list.appendChild(li)
-
insertAdjacentElement() 可以向元素任意的位置添加元素
-
两个参数, 1. 要添加的位置 2. 添加的元素
- beforeend 标签的最后 (当前元素子元素)
- afterbegin 标签的开始 (当前元素子元素)
- beforegin 在元素的前面插入元素(当前元素兄弟元素)
- afterend 在元素的后面插入元素(当前元素兄弟元素)
-
list.insertAdjacentElement(“afterbegin”, li)
-
insertAdjacentHTML 直接向网页中添加代码
-
// 获取ul
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.insertAdjacentHTML("beforeend", "<li id='bgj'>白骨精</li>")
}
const btn02 = document.getElementById("btn02")
btn02.onclick = function() {
// 创建蜘蛛精, 替换孙悟空
const li = document.createElement("li")
li.textContent = "蜘蛛精"
li.id = "zhj"
// 获取孙悟空
const swk = document.getElementById("swk")
// replaceWith() 使用一个元素替换当前元素
// 用li替换swk
// swk.replaceWith(li)
// remove() 用来删除当前元素
// swk.remove()
}
12. 练习
<div class="outer">
<table>
<tbody>
<tr>
<th>姓名</th>
<th>邮件</th>
<th>薪资</th>
<th>操作</th>
</tr>
<tr>
<td>孙悟空</td>
<td>swk@hgs.com</td>
<td>10000</td>
<td><a href="javascript:;">删除</a></td>
</tr>
<tr>
<td>猪八戒</td>
<td>zbj@glz.com</td>
<td>8000</td>
<td><a href="javascript:;">删除</a></td>
</tr>
<tr>
<td>沙和尚</td>
<td>shs@lsh.com</td>
<td>6000</td>
<td><a href="javascript:;">删除</a></td>
</tr>
</tbody>
</table>
<form action="#">
<div>
<label for="name">姓名</label>
<input type="text" id="name" />
</div>
<div>
<label for="email">邮件</label>
<input type="email" id="email" />
</div>
<div>
<label for="salary">薪资</label>
<input type="number" id="salary" />
</div>
<button>添加</button>
</form>
</div>
/*
点击删除超链接后, 删除当前的员工信息
*/
function delEmpHandler() {
// 本练习中的超链接我们不希望跳转, 但是跳转行为是超链接默认行为,
// 只要点击超链接就会触发页面中的跳转, 事件中可以取消默认行为来阻止超链接默认行为
// 使用return false来取消默认行为, 只有xxx.xxx = function() {}这种形式绑定的事件中才执行
// return false
// 删除当前员工信息, 删除当前元素所在的tr
// console.log(this)
// thi表示当前点击的超链接
const tr = this.parentNode.parentNode
// 获取要删除的员工姓名
// const empName = tr.getElementsByTagName("td")[0].textContent
const empName = tr.firstElementChild.textContent
// 弹出一个友好的提示
// confirm 确认取消删除
let flag = confirm("你确认要删除【" + empName + "】吗")
if (flag) {
tr.remove()
}
}
// 获取所有的超链接
const links = document.links
// 为它们绑定单击响应
for (let i = 0; i < links.length; i++) {
links[i].onclick = delEmpHandler
}
13. 练习更新
function delEmpHandler() {
const tr = this.parentNode.parentNode
const empName = tr.firstElementChild.textContent
let flag = confirm("你确认要删除【" + empName + "】吗")
if (flag) {
tr.remove()
}
}
const links = document.links
for (let i = 0; i < links.length; i++) {
links[i].onclick = delEmpHandler
}
/*
点击按钮后, 将用户信息插入到表格中
*/
// 获取tbody
const tbody = document.querySelector("tbody")
const btn = document.getElementById("btn")
btn.onclick = function () {
// 获取用户输入的数据
const name = document.getElementById("name").value
const email = document.getElementById("email").value
const salary = document.getElementById("salary").value
// 将获取到的数据设置为dom对象
// 这种写法容易被攻击, 有风险
tbody.insertAdjacentHTML("beforeend", `
<tr>
<td>${name}</td>
<td>${email}</td>
<td>${salary}</td>
<td><a href="javascript:;">删除</a></td>
</tr>
`)
// 由于上面的超链接是新添加的, 所以它的上面并没有绑定单击响应函数, 所以新添加的员工无法删除
// 解决方式, 为新添加的员工绑定响应函数
links[links.length - 1].onclick = delEmpHandler
}
14. 练习更新
function delEmpHandler() {
const tr = this.parentNode.parentNode
const empName = tr.firstElementChild.textContent
let flag = confirm("你确认要删除【" + empName + "】吗")
if (flag) {
trNaNpxove()
}
}
const links = document.links
for (let i = 0; i < links.length; i++) {
links[i].onclick = delEmpHandler
}
/*
点击按钮后, 将用户信息插入到表格中
*/
// 获取tbody
const tbody = document.querySelector("tbody")
const btn = document.getElementById("btn")
btn.onclick = function () {
// 获取用户输入的数据
const name = document.getElementById("name").value
const email = document.getElementById("email").value
const salary = document.getElementById("salary").value
// 将获取到的数据设置为dom对象
// 创建元素
const tr = document.createElement("tr")
// 创建td
const nameTd = document.createElement("td")
const emailTd = document.createElement("td")
const salaryTd = document.createElement("td")
// 添加文本
nameTd.textContent = name
emailTd.textContent = email
salaryTd.textContent = salary
// 将三个td添加到tr中
tr.appendChild(nameTd)
tr.appendChild(emailTd)
tr.appendChild(salaryTd)
tr.insertAdjacentHTML("beforeend", `<td><a href="javascript:;">删除</a></td>` )
tbody.appendChild(tr)
// 由于上面的超链接是新添加的, 所以它的上面并没有绑定单击响应函数, 所以新添加的员工无法删除
// 解决方式, 为新添加的员工绑定响应函数
links[links.length - 1].onclick = delEmpHandler
}
15. 节点复制
<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>
const list2 = document.getElementById("list2")
const l1 = document.getElementById("l1")
const btn01 = document.getElementById("btn01")
btn01.onclick = function() {
// cloneNode里面传入true和false ,
// true代表一起克隆. 不写或者false代表不克隆
const newl1 = l1.cloneNode(true) // 用来对节点进行复制的
/*
使用cloneNode节点进行复制的时候, 它会复制节点所有特点, 包括所有属性
这个方法默认只会复制当前节点. 不会复制节点的子节点
可以传递一个true作为参数, 这样该方法也会将元素的子节点一起克隆
*/
newl1.id = "newl1"
list2.appendChild(newl1)
}
16. 修改css样式
<button id="btn">点我一下</button>
<div class="box1"></div>
const btn = document.getElementById("btn")
const box1 = document.querySelector(".box1")
btn.onclick = function () {
btn.style.marginLeft = "500px"
// 修改box1的样式
// 修改样式的方法: 元素.style.样式名 = "样式值"
// 注意: 如果样式名中含有-, 则需要将样式名修改为驼峰命名法 background-color -- backgroundColor
box1.style.width = "400px"
box1.style.backgroundColor = "blue"
}
17. 读取css样式
- getComputedStyle()
- 它会返回一个对象, 这个对象中包含了当前元素所有生效的样式
- 参数
- 要获取样式对象
- 要获取的伪元素
- 返回值
- 返回的是一个对象, 对象中存储了当前元素样式
- 注意:
- 样式对象中返回的样式值不一定拿来直接计算
- 所以使用时, 一定要确保是可以计算的才去计算
const btn = document.querySelector("#btn")
const box = document.querySelector(".box")
btn.onclick = function() {
// 点击按钮读取css元素样式
const styleObj = getComputedStyle(box)
// console.log(styleObj.color) // 获取不到伪元素
// console.log(parseInt(styleObj.width) + 200 + "px")
// console.log(styleObj.backgroundColor)
console.log(styleObj.width)
const beforeStyle = getComputedStyle(box, "::before")
console.log(beforeStyle.color)
}
18. 通过属性读取样式
-
元素.clientHeight
-元素.clientWidth- 获取元素内部的宽度和高度(包括内容区和内边距)
-
元素.offsetHeight
-
元素.offsetWidth
- 获取元素可见框的大小(包括内容区, 内边距, 边框)
-
元素.scrollHeight
-
元素.scrollWidth
- 获取元素滚动区域的大小
-
元素.offsetParent
- 获取元素的定位父元素
- 离当前元素最近的开启了定位元素的父元素, 如果所有的元素都没有开启定位则返回body
-
元素.offsetTop
-
元素.offsetleft
- 获取元素相对于其定位元素的偏移量
-
元素.scrollTop
-
元素.scrollLeft
- 获取或设置元素滚动条的偏移量
19. 操作class
- 元素.classList.add() 向元素中添加一个或多个class
- 元素.calssList.remove() 移除元素中的一个或多个class
- 元素.classList.toggle() 切换元素中的一个class
- 元素.classList.contains() 检查元素是否包含某个属性
// 添加
box1.classList.add("box2, box3, box4")
box1.classList.add("box2")
// 删除
box1.classList.remove("box2")
// 切换
box1.classList.toggle("box2")
// 检查是否包含
let result = box1.classList.contains("box2")
console.log(result )
-
除了直接修改样式外, 也可以通过修改class属性来间接的影响
- 通过class修改样式的好处
- 可以一次性修改多个样式
- 对js和css进行解耦
- box1.className += " box2"
- 通过class修改样式的好处
-
元素.classList 是一个对象, 对象中提供了对当前元素的类的各种操作
20. 事件对象
- Event 事件
- 事件对象
- 事件对象是浏览器在事件触发时所创建的对象
- 这个对象中封装了事件相关的各种信息
- 通过事件对象可以获取到事件的详细信息
- 比如: 鼠标的坐标, 键盘的按键
- 浏览器在创建事件对象后, 会将事件对象作为响应函数的参数传递
- 所以我们可以在事件的回调函数中定义一个形参来接收事件对象
- 事件对象
- mousemove 鼠标移动
box1.addEventListener("mousemove", function(event) {
// console.log(event.clientX, event.clientY)
box1.textContent = event.clientX + ',' +event.clientY
})
21. 事件对象
-
在DOM中存在多种不同类事件对象
-
多种事件对象有一个共同的祖先 Event
- event.target 表示触发的事件
- event.currentTarget 绑定事件对象 (同this)
- event.stopPropagation() 停止事件的传导
- event.preventDefault() 取消默认行为 推荐使用这个, 不要使用return fasle
-
事件的冒泡(bubble)
- 事件的冒泡就是指事件的向上传递
- 当元素上的某个事件被触发后, 其祖先元素上的相同事件也会同时触发
- 冒泡的存在大大的简化了代码的编写, 但是在一些场景下我们并不希望冒泡的存在
- 不希望事件冒泡时, 可以通过事件对象来取消冒泡
-
-
event.target 表示的是触发事件的对象
const box1 = document.getElementById("box1")
const box2 = document.getElementById("box2")
const box3 = document.getElementById("box3")
const chao = document.getElementById("pop")
chao.addEventListener("click", function(event) {
event.preventDefault() // 取消默认行为
console.log("被点击了")
})
box1.addEventListener("click", function (event) {
/*
在事件的响应函数中
event.target 表示的是触发事件的对象
this 绑定事件的对象
*/
// console.log(event.target)
// event.currentTarget 当前的对象
console.log(event.currentTarget)
})
// box2.addEventListener("click", function(event) {
// event.stopPropagation() // 取消事件的传导
// console.log("我是box2")
// })
// box3.addEventListener("click", function(event) {
// event.stopPropagation() // 取消事件的传导
// console.log("我是box3")
// })
22. 事件冒泡
const box1 = document.getElementById("box1")
const box2 = document.getElementById("box2")
document.addEventListener("mousemove", function(event) {
box1.style.left = event.clientX + "px"
box1.style.top = event.clientY + "px"
})
box2.addEventListener("mousemove", function(event) {
event.stopPropagation()
})
23. 事件委派
- 思路:
- 可以将事件统一绑定个document, 这样点击超链接时由于事件冒泡
- 会导致document上的点击事件被触发, 这样只绑定一次, 所有的超链接都会被触发
const links = document.querySelectorAll("ul a")
const list = document.getElementById("list")
const btn = document.getElementById("btn")
// for (let i = 0; i < links.length; i++) {
// links[i].addEventListener("click", function(event) {
// console.log(event.target.textContent)
// })
// }
// // 点击按钮后, 在ul里添加一个新的li
// btn.addEventListener("click", function(event) {
// list.insertAdjacentHTML("beforeend", "<li><a href='Javascript:;'>新超链接</a></li>")
// })
document.addEventListener("click", function(event) {
console.log(event.target.textContent)
})
btn.addEventListener("click", function(event) {
list.insertAdjacentHTML("beforeend", "<li><a href='Javascript:;'>新超链接</a></li>")
})
24. 事件委派
- 事件委派
- 将本该绑定给多个元素的事件, 统一绑定给document, 这样可以降低代码的复杂度方便维护
const list = document.getElementById("list")
const btn = document.getElementById("btn")
// 获取list中的所有链接
const links = list.getElementsByTagName("a")
document.addEventListener("click", function (event) {
// 在执行代码之前, 先判断一下事件是由谁触发的
// 检查event.terget是否在links中存在
// console.log(Array.from(links))
if ([...links].includes(event.target)) {
console.log(event.target.textContent)
}
})
// 点击按钮后, 在ul中添加一个新的li
btn.addEventListener("click", function (event) {
list.insertAdjacentHTML("beforeend", "<li><a href='Javascript:;'>新超链接</a></li>")
})
25. 事件捕获
- 事件的传播机制(事件捕获)
-
在DOM中, 事件的传播可以分为三个阶段:
- 事件捕获 (由祖先元素向目标元素进行事件的捕获)(默认阶段, 事件不会在捕获阶段触发)
- 目标阶段 (触发事件的对象)
- 冒泡阶段 (由目标元素向祖先元素冒泡的阶段)
-
事件的捕获, 指事件从外向内的传导
-
当前元素上的触发事件以后, 会先从当前元素最大的祖先元素开始向当前元素开始进行事件捕获
-
如果希望在捕获阶段触发事件, 可以在addEventListener的第三个参数设置为true
- 一般情况下我们不希望在捕获阶段触发, 所以通常都不需要设置第三个参数
-
-
const box1 = document.getElementById("box1")
const box2 = document.getElementById("box2")
const box3 = document.getElementById("box3")
box1.addEventListener("click", function (event) {
// event.stopPropagation() // 停止事件的传导, 如果是冒泡就把冒泡停了, 如果是捕获就把捕获停了
// 这样 2 3 都不会显示了
alert(event.eventPhase) // eventPhase 表示事件触发的时机
// 返回1表示捕获阶段, 2目标节点
}, )
box2.addEventListener("click", function (event) {
alert(event.eventPhase)
}, )
box3.addEventListener("click", function (event) {
alert(event.eventPhase)
}, )
26. BOM
-
BOM
- 浏览器对象模型
- BOM为我们提供了一组对象, 通过这组对象可以完成对浏览器的各种操作
-
BOM对象:
- Window - 代表浏览器窗口 (全局对象)
- Navigator - 浏览器的对象 (可以用来识别浏览器)
- Location - 浏览器地址栏信息
- History - 浏览器历史记录 (控制浏览器的前进后退)
- Screen - 屏幕的信息 (用的比较少)
-
BOM对象都是作为window对象的属性保存的, 所以可以直接在JS中访问这些对象
27. navigator
- Navigator
- userAgent 返回一个用来描述浏览器信息的字符串
console.log(navigator.userAgent)
28. location
- Location 表示浏览器地址栏的信息
- 可以直接将location的值修改为一个新的地址, 这样会使的网页发生跳转
- location.href 可以获取当前的地址
- location.assign() 跳转到新的页面
- location.replace() 使用新网址替换当前网址, (不会产生历史记录, 无法通过回退按钮回退)
- location.reload() 刷新页面, 可以传递一个true来强制缓存的刷新
const btn = document.getElementById("btn")
btn.addEventListener("click", function(event) {
// console.log(location)
// location = "https://www.lilichao.com"
// console.log(location.href)
// location.assign("https://www.lilichao.com")
// location.replace("https://www.lilichao.com")
location.reload(true)
})
29. Histort
- History
- history.back()
- 回退
- history.forward()
- 前进
- history.go()
- 可以向前跳转(正数向前), 也可以向后跳转(负数向后), 还可以指定数量
- history.back()
const btn = document.getElementById("btn")
btn.addEventListener("click", function(event) {
// console.log(History.length)
// history.back()
// history.forward()
// history.go(-1)
})
30. 定时器
- 通过定时器, 可以使代码在指定时间后执行
- 设置定时器的方式有两种:
-
setTimeout()
- 参数
- 回调函数 (要执行的代码)
- 间隔的时间(毫秒)
- 参数
-
setTimeout()只会执行一次
-
关闭定时器
- clearTimeout() 里面传定时器的标识
-
setInterval()
- 参数
3. 回调函数
4. 间隔的时间
- 参数
-
setinterval()会重复执行
-
关闭定时器
- clearInterval() 里面传定时器的标识
-
-
- 设置定时器的方式有两种:
// let time = setTimeout(function() {
// console.log("我是定时器中的代码")
// }, 3000)
// const btn = document.querySelector("button")
// btn.onclick = function() {
// clearTimeout(time)
// }
// let interval = setInterval(function() {
// console.log("哈哈哈哈")
// }, 2000)
// const btn = document.querySelector("button")
// btn.onclick = function() {
// clearInterval(interval)
// }
let numH1 = document.getElementById("num")
num = 0
let inter = setInterval(() => {
num++
numH1.textContent = num
if (num === 10) {
clearInterval(inter)
}
}, 1000)
31. 事件循环
-
事件循环 (event loop)
- 函数在每次执行时, 都会产生执行环境
- 执行环境负责存储函数执行时产生的一切数据
- 问题: 函数的执行环境要存储到哪里呢?
- 函数的执行环境存储到了一个叫调用栈的地方
- 栈, 是一种数据结构, 特点: 后进先出
-
调用栈 (call stack)
- 调用栈负责存储函数的执行环境
- 当一个函数被调用时, 它的执行环境会作为一个栈帧
- 插入到调用栈的栈顶, 函数执行完毕其栈帧会自动从栈中弹出
32. 事件循环
-
事件循环 (event loop)
- 函数在每次执行时, 都会产生执行环境
- 执行环境负责存储函数执行时产生的一切数据
- 问题: 函数的执行环境要存储到哪里呢?
- 函数的执行环境存储到了一个叫调用栈的地方
- 栈, 是一种数据结构, 特点: 后进先出
- 队列, 是一种数据结构, 特点: 先进先出
-
调用栈 (call stack)
- 调用栈负责存储函数的执行环境
- 当一个函数被调用时, 它的执行环境会作为一个栈帧
- 插入到调用栈的栈顶, 函数执行完毕其栈帧会自动从栈中弹出
-
消息队列
- 消息队列负责存储将要执行的函数
- 当我们触发了一个事件时, 其响应函数并不是直接就添加到调用栈中的
- 因为调用栈中可能会存在一些还没有执行完毕的代码
- 事件触发后, JS引擎是将事件响应函数插入到消息队列排队
function fn() {
let a = 10
let b = 20
function fn2() {
console.log("fn2")
}
fn2()
}
fn()
console.log(1111)
const btn = document.getElementById("btn")
btn.onclick = function () {
console.log("被点击了")
}
33. 综合练习-贪吃蛇
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#main {
height: 460px;
width: 360px;
border: 10px #000 solid;
background-color: #b7d4a8;
border-radius: 20px;
margin: 50px auto;
}
#stage {
width: 304px;
height: 304px;
border: 2px solid #000;
margin: 20px auto;
/* 相对定位 */
position: relative;
}
#snake>div {
width: 10px;
height: 10px;
background-color: #000;
position: absolute;
border: 1px solid #b7d4a8;
}
#food {
width: 10px;
height: 10px;
/* 绝对定位 */
position: absolute;
top: 100px;
left: 120px;
display: flex;
flex-flow: wrap;
}
#food>div {
width: 5px;
height: 5px;
background-color: #000;
transform: rotate(40deg);
}
#info {
width: 304px;
margin: 0 auto;
display: flex;
/* 文字左右对齐 */
justify-content: space-between;
font-weight: 700;
font-size: 20px;
font-family: courier;
}
<!-- 外边大框 -->
<div id="main">
<!-- 里面的游戏区域 -->
<div id="stage">
<!-- 蛇 -->
<div id="snake">
<div></div>
</div>
<!-- 果实 -->
<div id="food">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<!-- 分数 -->
<div id="info">
<div>SCORE: <span id="score">0</span></div>
<div>LEVEL: <span id="level">1</span></div>
</div>
</div>
// 获取蛇的容器
const snake = document.getElementById("snake")
//获取蛇的各个部分
const snakes = snake.getElementsByTagName("div")
// 获取食物
const food = document.getElementById("food")
// 获取计分板
const scoreSpan = document.getElementById("score")
const levelSpan = document.getElementById("level")
// 创建变量存储分数登记
let score = 0
let level = 0
/*
食物的取值坐标应该在0 = 290之间
*/
function changeFood() {
// 生成 0 - 29之间的随机数 然后再乘以10
const x = Math.floor(Math.random() * 30) * 10
const y = Math.floor(Math.random() * 30) * 10
// 设置食物的坐标
food.style.left = x + "px"
food.style.top = y + "px"
}
// 定义一个变量用来存储蛇的移动方向
let dir
// 创建一个变量来记录按键的状态
let keyActive = true
/*
绑定时间keydown按下 keyup松开
- 键盘事件只能绑定给可以获取焦点的元素或者是document
*/
const keyArr = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight",]
// 创建一个对象
const reObj = {
ArrowUp: "ArrowDown",
ArrowDown: "ArrowUp",
ArrowLeft: "ArrowRight",
ArrowRight: "ArrowLeft",
}
/*
游戏进制掉头,
构成的要件
1. 身体超过2包括2
2. 方向不能是相反的方向
处理:
保持原来的方向不变, 不修改dir的值
*/
document.addEventListener("keydown", function (event) {
if (keyActive && keyArr.includes(event.key)) {
// 判断蛇是否掉头
if (snakes.length < 2 || reObj[dir] !== event.key) {
// 设置方向
dir = event.key
keyActive = false
}
}
})
/*
要使得身体和头一起移动, 只需要在蛇移动时, 变化蛇尾巴的位置
*/
// 废弃了
// let isGameOver = false
setTimeout(function move() {
// 蛇头
const head = snakes[0]
// 获取蛇头坐标
let x = head.offsetLeft
let y = head.offsetTop
switch (dir) {
case "ArrowUp":
// 向上移动
y = y - 10
break
case "ArrowDown":
// 向下移动
y += 10
break
case "ArrowLeft":
// 向左移动
x = x - 10
break
case "ArrowRight":
// 向右移动蛇
x += 10
break
}
// 检查蛇是否吃到食物
if (head.offsetTop === food.offsetTop && head.offsetLeft === food.offsetLeft) {
// console.log("吃到食物了")
// 1. 改变食物的位置
changeFood()
// 2. 增加蛇的身体
snake.insertAdjacentHTML("beforeend", "<div/>")
score++
scoreSpan.textContent = score
// 检查等级
if(score % 1 === 0 && level < 14) {
level++
levelSpan.textContent = level + 1
}
}
/*
判断蛇是否结束:
1. 撞墙
2. 撞自己
*/
// 判断撞墙
if (x < 0 || x > 290 || y < 0 || y > 290) {
alert("撞墙了, 游戏结束!")
// 游戏结束
return
}
// 判断是否撞到自己
for (let i = 0; i < snakes.length - 1; i++) {
if (snakes[i].offsetLeft === x && snakes[i].offsetTop === y) {
alert("撞到自己了")
return
}
}
// 获取尾巴
const tail = snakes[snakes.length - 1]
// 移动蛇的位置
tail.style.left = x + "px"
tail.style.top = y + "px"
// 将尾巴移动到蛇头的位置
snake.insertAdjacentElement("afterbegin", tail)
keyActive = true
setTimeout(move, 300 - level * 20)
}, 300)