Web APIs 第四天
DOM- 事件高级
事件对象是什么?
- 也是个对象,这个对象里有事件触发时的相关信息
事件对象在哪里?
- 在事件绑定的回调函数的第一个参数就是事件对象
- 能够使用常见事件对象属性一般命名为event、ev、e
元素.addEventLIstener('click',function(e)){
}
常用属性
- type: 获取当前的事件类型
- clientX/clientY:获取光标相对于浏览器可见窗口左上角的位置
- key: 用户按下的键盘键的值 ,现在不提倡使用keyCode
案例 跟随鼠标案例
- 需求:一张图片一直跟着鼠标移动 分析:
- 鼠标在页面中移动,用到 mousemove 事件
- 不断把鼠标在页面中的坐标位置给图片left和top值即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
/>
<title>04-小鸟跟随鼠标.html</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* div {
width: 100px;
height: 100px;
background-color: aqua;
position: fixed;
} */
img {
position: fixed;
/* width: 50px; */
/* 移动自身宽度和高度的一半 */
transform: translate(-50%, -50%);
}
body {
height: 100vh;
/* 隐藏鼠标 */
cursor: none;
}
</style>
</head>
<body>
<h1>用户鼠标不要移动那么快</h1>
<!-- <div></div> -->
<img src="./images/xn.png" alt="" />
<script>
/*
1 给body标签 绑定 鼠标移动事件
body也是一个普通的块级元素 高度由内容撑开 同时 div使用了定位-脱标 body标签没有高度
2 事件触发了 获取 事件对象的坐标 clientX
3 把坐标设置给div的left top
*/
// const div = document.querySelector('div');
const img = document.querySelector('img');
document.body.addEventListener('mousemove', function (event) {
const left = event.clientX;
const top = event.clientY;
img.style.left = left + 'px';
img.style.top = top + 'px';
});
</script>
</body>
</html>
按下回车发布微博案例
-
需求:按下回车键盘,可以发布信息 分析:
-
用到按下键盘事件 keydown 或者 keyup 都可以
-
如果用户按下的是回车键盘,则发布信息
-
按下键盘发布新闻,其实和点击发布按钮效果一致 send.click()
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
/>
<title>06-微博发布.html</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>
</head>
<body>
<a href="http://www.baidu.com">百</a>
<textarea id="area"></textarea>
<button>发布</button>
<ul></ul>
<script>
/*
步骤
1 给发布按钮 绑定点击事件
1 获取文本域的内容
2 拼接到一个新创建的li标签中
3 把新创建的li标签插入到ul中
2 给文本域 绑定键盘按下事件
按下键盘之后 先判断当下按下的是不是回车 也去执行点击按钮的工作 即可
*/
const btn = document.querySelector('button');
const area = document.querySelector('#area');
const ul = document.querySelector('ul');
btn.addEventListener('click', function (event) {
// 判断一下当前的文本域的内容 是不是空字符串
if (area.value.trim() === '') {
console.log('返回不再往下执行');
return;
}
// area.value
const li = document.createElement('li');
li.innerText = area.value;
ul.appendChild(li);
});
area.addEventListener('keydown', function (event) {
// 判断按键是不是回车键
if (event.key === 'Enter') {
// console.log("执行和按钮一样的功能");
// 给btn按钮绑定过点击事件,点击事件也是可以主动触发
btn.click(); // 你点击了一下按钮
// 解决按下回车 文本换行的效果
// 文本域的默认的行为
// 处理标签的默认行为
// return false
event.preventDefault(); // 阻止浏览器标签的默认行为 阻止a标签默认跳转行为
}
});
document.querySelector('a').addEventListener('click', function (e) {
e.preventDefault(); // 阻止a标签的默认 跳转行为
});
</script>
</body>
</html>
事件流
事件流经过的2个阶段
事件流指的是事件完整执行过程中的流动路径
-
捕获和冒泡
-
捕获阶段: 父节点 流动到 子节点
-
冒泡节点: 子节点 流动到 父节点
-
可以修改触发事件,让他选择使用 捕获还是冒泡节点
-
总结
1.捕获和冒泡
2.默认情况 冒泡 如果想要修改 可以 addEventListener 第三个参数 传入 true即可
3 以后的代码开发过程中,还是继续使用默认的 冒泡阶段
假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
捕获阶段:从父到子。 冒泡阶段:从子到父
事件捕获
事件捕获是指事件被触发时从DOM根元素开始去执行对应的事件(从里到外)
捕获阶段是 从父到子 冒泡阶段是从子到父
事件捕获需要写相应的代码才能看到效果:
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
若在是否使用捕获机制处传入 true 则代表是捕获阶段触发,默认为false,代表冒泡阶段触发 若是用L0事件监听(元素.onclick)则只有冒泡阶段,没有捕获
代码
<!DOCTYPE html>
<html lang="en">
<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>
.father {
margin: 100px auto;
width: 500px;
height: 500px;
background-color: pink;
}
.son {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
let fa = document.querySelector('.father')
let son = document.querySelector('.son')
fa.addEventListener('click', function () {
alert('我是爸爸')
}, true)
son.addEventListener('click', function () {
alert('我是儿子')
}, true)
document.addEventListener('click', function () {
alert('我是爷爷')
}, true)
// btn.onclick = function() {}
</script>
</body>
</html>
事件冒泡
在一个对象上触发某类事件(比如单击onclick事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即document对象(有些浏览器是window)。
打个比方说:你在地方法院要上诉一件案子,如果地方没有处理此类案件的法院,地方相关部门会帮你继续往上级法院上诉,比如从市级到省级,直至到中央法院,最终使你的案件得以处理。
事件冒泡有什么作用
事件冒泡允许多个操作被集中处理(把事件处理器添加到一个父级元素上,避免把事件处理器添加到多个子级元素上),它还可以让你在对象层的不同级别捕获事件。
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
当一个元素被触发事件后,会依次向上调用所有父级元素的同名事件
事件捕获和事件冒泡属于两个相反的过程。
阻止冒泡
因为默认就有冒泡模式的存在,所以就容易导致事件影响到父级。
组织事件流动需要拿到事件对象,语法:事件对象.stopPropagation()
此方法可以阻断事件流动传播,不仅阻断了冒泡阶段,在捕获阶段也有效
mouseover和mouseout会有冒泡效果mouseenter和mouseleave没有冒泡效果
阻止默认行为
比如点击链接不跳转,点击表单不提交等。语法:e.preventDefault()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
/>
<title>03-阻止冒泡</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
width: 200px;
height: 200px;
padding: 20px;
overflow: hidden;
}
.a {
background-color: red;
}
.b {
background-color: blue;
}
.c {
background-color: green;
}
p {
width: 200px;
height: 200px;
background-color: yellow;
}
</style>
</head>
<body>
<p>1</p>
<div class="a">
爷
<div class="b">
爸
<div class="c">儿</div>
</div>
</div>
<script>
const p = document.querySelector('p');
const a = document.querySelector('.a');
a.addEventListener(
'click',
function () {
p.style.backgroundColor = 'red';
p.innerText = +p.innerText + 1;
console.log('a');
}
);
const b = document.querySelector('.b');
b.addEventListener(
'click',
function (event) {
console.log('b');
p.innerText = +p.innerText + 10;
p.style.backgroundColor = 'blue';
// 阻止事件冒泡
event.stopPropagation();
}
);
const c = document.querySelector('.c');
c.addEventListener(
'click',
function (event) {
console.log('c');
p.innerText = +p.innerText + 100;
p.style.backgroundColor = 'green';
// 阻止事件冒泡
event.stopPropagation();
}
);
/*
引出新的知识 阻止冒泡! 下一节课 优雅的方式来解决刚才的问题!
在事件对象中 event 找到一个方法 停止冒泡 event.stopPropagation();
*/
</script>
</body>
</html>
阻止标签的默认行为
使用 event.preventDefault()
const a=document.querySelector("a");
const form=document.querySelector("form");
const button=document.querySelector("button");
a.addEventListener("click",function (event) {
console.log("a标签的点击触发啦");
// 阻止a标签的默认行为,让他不要跳转
event.preventDefault();
})
案例:鼠标右键-菜单
<!DOCTYPE html>
<html lang="en">
<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>
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
height: 100vh;
}
.menu{
list-style: none;
padding:10px;
border-radius: 5px;
border: 1px solid #ccc;
width: 150px;
position: fixed;
display: none;
}
li{
height: 40px;
display: flex;
align-items: center;
padding-left: 10px;
border-bottom: 1px solid #ccc;
}
li:hover{
background-color: green;
color:#fff;
cursor: pointer;
}
li:last-child{
border-bottom: none;
}
</style>
</head>
<body>
<ul class="menu">
<li>添加图标</li>
<li>切换壁纸</li>
<li>下载壁纸</li>
<li>设置</li>
</ul>
<script>
const menu = document.querySelector('.menu')
//
document.body.addEventListener('contextmenu',function(event){
event.preventDefault()
const left=event.clientX
const top =event.clientY
menu.style.display='block'
menu.style.left =left+'px'
menu.style.top =top+'px'
})
document.body.addEventListener('click',function(){
menu.style.display='none'
})
</script>
</body>
</html>
事件委托
事件委托是指利用事件流的特征解决一些开发需求的技巧
优点:给父级元素加事件(可以提高性能)
原理:事件委托其实是利用事件冒泡的特点,给父元素添加事件,子元素可以触发
实现:事件对象.target可以获得真正触发事件的元素
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。(所有用到按钮的事件,多数的鼠标事件和键盘事件) 值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。 不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。
// 事件委托
// 本来想要给li标签绑定事件实现业务
// 把事件绑定写在 父元素上 把li标签应该要做的事情 委托给父元素来做!
const ul=document.querySelector("ul");
ul.addEventListener("click",function (event) {
// event.target 有可能是ul标签,有可能是li标签,还有可能是 a标签
// 一会再来解决这个问题。
event.target.style.backgroundColor="red";// 不够完美的
// 事件委托 绑定写在父元素 把li标签 要在
console.log(event.target);
// event.target 你当前点击的是哪个标签(点击最深最底层的那个标签即可)
// console.log(event.target);// 获取到被点击的li标签
})
event.target.nodeName
<script>
const ul = document.querySelector('ul')
ul.addEventListener('click',function(event){
// 点击li标签才触发
if(event.target.nodeName ==="LI"){
console.log('改变颜色')
event.target.style.backgroundColor='green'
}
})
</script>
综合案例
<h1>新增学员</h1>
<div class="info">
姓名:<input type="text" class="uname" /> 年龄:<input
type="text"
class="age"
/>
性别:
<select name="gender" id="" class="gender">
<option value="男">男</option>
<option value="女">女</option>
</select>
薪资:<input type="text" class="salary" /> 就业城市:<select
name="city"
id=""
class="city"
>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="曹县">曹县</option>
</select>
<button class="add">录入</button>
</div>
<h1>就业榜</h1>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>薪资</th>
<th>就业城市</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- <tr>
<td>1</td>
<td>这是名称</td>
<td>这是年龄</td>
<td>这是性别</td>
<td>这是工资</td>
<td>这是所在城市</td>
<td>
<a href="javascript:" class="del">删除</a>
</td>
</tr> -->
</tbody>
</table>
<script>
let arr=[]
let tbody = document.querySelector('tbody')
let uname =document.querySelector('.uname')
let age = document.querySelector('.age')
let gender = document.querySelector('.gender')
let salary = document.querySelector('.salary')
let city = document.querySelector('.city')
let add = document.querySelector('.add')
renderTableByArr()
// 按钮绑定点击事件
add.addEventListener('click',function(){
const data ={
// 学号
id:Date.now(),
uname:uname.value,
age:age.value,
gender:gender.value,
salary:salary.value,
city:city.value,
}
arr.push(data);
// console.log(arr);
renderTableByArr()
uname.value=''
age.value=''
gender.value='男'
salary.value=''
city.value='北京'
})
tbody.addEventListener('click',function(event){
// console.log('ee');
if(event.target.nodeName==='A'){
// console.log('a');
const index =event.target.dataset.index
arr.splice(index,1);
renderTableByArr()
}
})
function renderTableByArr(){
let html =``;
for(let index=0;index<arr.length;index++){
html+=`
<tr>
<td>${arr[index].id}</td>
<td>${arr[index].uname}</td>
<td>${arr[index].age}</td>
<td>${arr[index].gender}</td>
<td>${arr[index].salary}</td>
<td>${arr[index].city}</td>
<td>
<a href="javascript:" class="del">删除</a>
</td>
</tr>`;
console.log(arr);
}
console.log(html);
tbody.innerHTML=html
console.log(arr+'1');
}
</script>
滚动事件和加载事件
滚动事件
当页面滚动时触发的事件
事件名:scroll
样例,监听整个页面滚动:
监听页面所有资源加载完毕
window.addEventListener('scroll',function(){
//执行操作
})
加载事件
加载外部资源(如图片、外联css和javaScript等) 加载完毕是触发的事件
事件名:load
注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
//页面加载事件
window.addEventListener('load',function(){
//执行的操作
})
两个加载事件的区别
load事件
给window加,监听整个页面资源
DOMContentLoaded
给document加,当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded事件被触发,而无需等待样式表、图像等完全加载。
document.addEvenListener('DOMContentLoaded',function(){
//执行的操作
})
注意事项
document.documentElement HTML 文档返回对象为HTML元素
scroll家族
使用场景: 我们想要页面滚动一段距离,比如100px,就让某些元素
显示隐藏,那我们怎么知道,页面滚动了100像素呢?
获取宽高
-
获取元素的内容总宽高(不包含滚动条)返回值不带单位
-
scrollWidth和scrollHeight
获取位置
-
获取元素内容往左、往上滚出去看不到的距离
-
scrollLeft和scrollTop
-
这两个属性是可以修改的
div.addEvenListener('scroll',function(){
console.log(this.scrollTop)
})
样例代码:
let div = document.querySelector('div')
console.log(div.scrollWidth)
div.addEventListener('scroll',function(){
console.log(this.scrollTop)
})
检测页面滚动事件
window.addEventListener('scroll',function(){
let num = document.documentElement.scrollTop
console.log(num)
})
-
documentElement 也就是指 html
offset
获取宽高
-
获取元素的自身宽高、包含元素自身设置的宽高、padding、border
-
offsetWidth和offsetHeight
获取位置
-
获取元素距离自己定位父级元素的左、上距离
-
offsetLeft和offsetTop 注意是只读属性
offsetWidth和offsetHeight是得到元素什么的宽高
- 内容 + padding + border
offsetTop和offsetLeft 得到位置以谁为准?
- 带有定位的父级
- 如果都没有则以 文档左上角 为准
client家族
clientWidth和clientHeight
-
获取元素的可见部分宽高(不包含边框,滚动条等)
-
获取左边框和上边框宽度
-
clientLeft和clientTop 注意是只读属性
window.addEventListener('resize',function(){
let w = document.documentElement.clientWidth
if(w >= 1920){
document.body.style.backgroundColor = 'pink'
}
else if(w?540){
document.body.style.backgroundColor = 'hotpink'
}
else{
document.body.style.backgroundColor = 'deeppink'
}
})