01-事件对象
- 事件对象:就是一个对象,里面包含着与当前事件相关的信息
- 获取对象:事件处理函数的第一个参数就是事件对象
<body>
<button>点击</button>
<input type="text" />
<script>
let btn = document.querySelector('button')
let input = document.querySelector('input')
// 鼠标按键事件
// 事件处理函数的第一个参数就是 事件对象,里面有当前事件的相关信息
btn.addEventListener('click', function(e) {
console.log(e)
})
// 键盘按键事件
input.addEventListener('input', function(e) {
console.log(e)
})
</script>
</body>
复制代码
02-事件对象常用属性
-
事件类型不一样,属性成员不一样
-
鼠标事件
- clientX,clientY:参照浏览器可见区域的左上角
- offsetX,offsetY:参照元素左上角
-
键盘事件
- key:按下的键的名称
- which:按下的键的键码----Enter:13 Esc:27
-
e.target:真正触发事件的对象
<style>
body {
height: 3000px;
}
div {
width: 3000px;
height: 300px;
background-color: pink;
margin: 100px;
}
</style>
</head>
<body>
<div></div>
<script>
let div = document.querySelector('div')
div.addEventListener('click', function(e) {
console.log(e)
})
// 可以为文档添加按键事件
document.addEventListener('keydown', function(e) {
console.log(e)
})
</script>
</body>
复制代码
03-跟随鼠标的天使
<style>
html,
body {
width: 100%;
height: 100%;
background-color: #ff0;
}
img {
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<img src="./images/tianshi.gif" alt="" />
<script>
let img = document.querySelector('img')
// 为页面添加鼠标移动的监听事件:以后如果你不知道为谁添加事件绑定,就为document
document.addEventListener('mousemove', function(e) {
console.log(e.clientX, e.clientY)
img.style.top = e.clientY - 40 + 'px'
img.style.left = e.clientX - 40 + 'px'
})
</script>
</body>
复制代码
04-事件流(捕获事件 冒泡事件)
-
触发事件时的事件流向
- 说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
-
事件捕获:从外到内,从上到下,从父到子
- 模拟捕获过程:第三个参数添加true
-
事件冒泡:从内到外,从下到上,从子到父
- 子元素触发事件之后,还会将事件冒泡给父容器
- 如果父容器添加了同名事件,父容器的事件也会触发
-
阻止事件传播
- 可以阻止捕获也可以阻止冒泡
- e.stopPropagation()
-
阻止默认行为
-
场景
- 阻止超链接的跳转
- 阻止表单元素csubmit的默认提交行为
-
e.preventDefault()
-
4.1 捕获事件
-
事件捕获概念:
- 从DOM的根元素开始去执行对应的事件 (从外到里)
- 事件捕获需要写对应代码才能看到效果
-
代码:
- DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
-
说明:
- addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
- 若传入false代表冒泡阶段触发,默认就是false
- 若是用 L0 事件监听,则只有冒泡阶段,没有捕获
<!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>
html,
body {
width: 100%;
height: 100%;
}
.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 father = document.querySelector('.father')
let son = document.querySelector('.son')
// 第三个参数为true就代表事件在捕获阶段触发,如果没有设置默认为false,false代表冒泡阶段触发
document.addEventListener(
'click',
function(e) {
console.log('document')
// 阻止事件流动可以阻止事件捕获,也可以阻止事件冒泡
e.stopPropagation()
},
true
)
document.documentElement.addEventListener(
'click',
function() {
console.log('documentElement')
},
true
)
document.body.addEventListener(
'click',
function() {
console.log('body')
},
true
)
father.addEventListener(
'click',
function() {
console.log('father')
},
true
)
son.addEventListener(
'click',
function() {
console.log('son')
},
true
)
</script>
</body>
</html>
复制代码
4.2 冒泡事件
-
事件冒泡概念:
- 当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
-
简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
-
事件冒泡是默认存在的
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
let father = document.querySelector('.father')
let son = document.querySelector('.son')
// 第三个参数为true就代表事件在捕获阶段触发,如果没有设置默认为false,false代表冒泡阶段触发
document.addEventListener('click', function(e) {
console.log('document')
// 阻止事件流动可以阻止事件捕获,也可以阻止事件冒泡
// e.stopPropagation()
})
document.documentElement.addEventListener('click', function() {
console.log('documentElement')
})
document.body.addEventListener('click', function() {
console.log('body')
})
father.addEventListener('click', function(e) {
console.log('father')
e.stopPropagation()
})
son.addEventListener('click', function(e) {
console.log('son')
})
</script>
</body>
复制代码
4.3 阻止元素的默认行为
-
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
-
若想把事件就限制在当前元素内,就需要阻止事件流动
-
阻止事件流动需要拿到事件对象
-
语法:
- 事件对象.stopPropagation()
-
此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
<style>
.father {
margin: 100px auto;
width: 500px;
height: 500px;
background-color: pink;
}
.son {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
</head>
<body>
<a href="http://www.baidu.com">我不想去百度啊</a>
<script>
let a = document.querySelector('a')
a.addEventListener('click', function(e) {
// 阻止默认行为:阻止元素的默认行为,后期只有两个场景
// 1。超链接的跳转
// 2。表单元素submit的默认提交行为
e.preventDefault()
console.log('自己做处理,不去跳转')
})
</script>
</body>
</html>
复制代码
05-事件委托
-
表现形式:将事件绑定给已存在的父容器,每一个子元素都能触发
-
好处:
- 简化代码
- 对已有元素和未来元素都有效
- 给父级元素加事件(可以提高性能)
-
原理:是利用了事件冒泡的原理:子元素触发事件,也会将事件冒泡给父容器
-
实现:事件对象.target 可以获得真正触发事件的元素
-
重点细节
-
事件绑定给父容器(父窗口一定要已经存在)
-
通过e.target获取真正触发事件的元素
-
一般情况下,需要判断是否有需要的元素才进行相应的业务处理
- localName判断元素类型
- className判断样式
- classList.contains判断是否包含某个样式标识
-
5.1 为什么需要事件委托
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
div {
width: 300px;
height: 300px;
border: solid;
}
</style>
</head>
<body>
<div>
<p>我是现成的p元素1</p>
<p>我是现成的p元素2</p>
</div>
<button>添加一个新的元素</button>
<script>
let btn = document.querySelector('button')
let div = document.querySelector('div')
let divps = document.querySelectorAll('div > p')
// divps.forEach(function(ele) {
// ele.addEventListener('click', function() {
// console.log(123)
// })
// })
btn.addEventListener('click', function() {
let newP = document.createElement('p')
newP.innerText = '我也是p元素,我是新来的'
div.appendChild(newP)
// newP.addEventListener()
})
// 这种将事件绑定给父容器,从而让每一个子元素 都能触发事件的方式就叫 ---- 事件委托
// 事件委托是利用了事件冒泡的原理:子元素会将事件传递给父容器
// 1。父容器必须已存在
// 2。这种绑定对已有子元素和未来子元素都有效
div.addEventListener('click', function() {
console.log(123)
})
</script>
</body>
</html>
复制代码
5.2 基于冒泡的原理我们可以将子元素的事件绑定给父容器
<body>
<ul>
<li>第1个li元素</li>
<li>第2个li元素</li>
<li>第3个li元素</li>
<li>第4个li元素</li>
<li>第5个li元素</li>
<li>第6个li元素</li>
</ul>
<script>
// let lis = document.querySelectorAll('li')
// lis.forEach(function(ele) {
// ele.addEventListener('click', function() {
// console.log(123)
// })
// })
let ul = document.querySelector('ul')
ul.addEventListener('click', function() {
console.log(123)
})
</script>
</body>
复制代码
5.3 事件委托
<body>
<div class="box">
<button>添加li元素</button>
<ul>
<p>aaa</p>
<p>aaa</p>
<p>aaa</p>
<p>aaa</p>
<li class="comm myli">我是第1个小li</li>
<li class="comm myli">我是第2个小li</li>
<li class="comm myli">我是第3个小li</li>
<li class="comm myli">我是第4个小li</li>
<li class="comm myli">我是第5个小li</li>
</ul>
</div>
<script>
let ul = document.querySelector('ul')
let button = document.querySelector('button')
// 添加委托事件:绑定给父容器
ul.addEventListener('click', function(e) {
// 将li元素设置为红色
// 1.如何拿到当前真正触发事件的元素
console.log(e)
// 2.判断当前目标元素是否是我需要的元素
// if (e.target.className == 'myli') {
if (e.target.classList.contains('myli')) {
e.target.style.color = 'red'
}
})
</script>
</body>
复制代码
用户信息案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<link rel="stylesheet" href="css/user.css" />
</head>
<body>
<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>
// 模拟数据:数据一定要有id做为数据的唯一标识,这个标识是以后数据 修改和删除的凭据
let students = [
{
id: 1,
name: '刘奕',
age: 18,
gender: '女',
salary: 20000,
city: '广州'
},
{
id: 2,
name: '王涛',
age: 19,
gender: '男',
salary: 20000,
city: '广州'
},
{
id: 3,
name: '泉林',
age: 18,
gender: '女',
salary: 30000,
city: '广州'
},
{
id: 4,
name: '王鹏',
age: 19,
gender: '男',
salary: 40000,
city: '广州'
}
]
// 获取元素
let tbody = document.querySelector('tbody')
let add = document.querySelector('.add')
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')
// 1.数据渲染
function init() {
// 遍历拼接
let htmlStr = ''
students.forEach(function(value, index) {
htmlStr += `<tr>
<td>${index + 1}</td>
<td>${value.name}</td>
<td>${value.age}</td>
<td>${value.gender}</td>
<td>${value.salary}</td>
<td>${value.city}</td>
<td>
<a href="javascript:" class="del" id='${
value.id
}'>删除</a>
</td>
</tr>`
})
tbody.innerHTML = htmlStr
}
init() // 一开始需要调用函数,否则函数不会自动的执行
// 2.委托方式绑定事件,一般情况下是绑定给最近的一级父容器
tbody.addEventListener('click', function(e) {
// 判断是否是单击了删除按钮
if (e.target.className == 'del') {
// 实现删除:filter方法的使用
// filter是数组非变更方法,所以要将fitler的结果覆盖原数组
students = students.filter(function(value) {
// 假如删除id为1的值
return value.id != e.target.id
})
// 重新渲染
init()
}
})
// 3.实现数据的新增
let id = 5
add.addEventListener('click', function() {
// 准备数据
let newObj = {
id: id,
name: uname.value,
age: age.value,
gender: gender.value,
salary: salary.value,
city: city.value
}
// 为下一次新增做准备,将id+1
id++
// 将数据添加到数组
students.push(newObj)
// 重新渲染
init()
})
</script>
</body>
</html>
复制代码
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color:#721c24;
}
h1 {
text-align: center;
color:#333;
margin: 20px 0;
}
table {
margin:0 auto;
width: 800px;
border-collapse: collapse;
color:#004085;
}
th {
padding: 10px;
background: #cfe5ff;
font-size: 20px;
font-weight: 400;
}
td,th {
border:1px solid #b8daff;
}
td {
padding:10px;
color:#666;
text-align: center;
font-size: 16px;
}
tbody tr {
background: #fff;
}
tbody tr:hover {
background: #e1ecf8;
}
.info {
width: 900px;
margin: 50px auto;
text-align: center;
}
.info input {
width: 80px;
height: 25px;
outline: none;
border-radius: 5px;
border:1px solid #b8daff;
padding-left: 5px;
}
.info button {
width: 60px;
height: 25px;
background-color: #004085;
outline: none;
border: 0;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
.info .age {
width: 50px;
}
复制代码
购物车案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>购物车全选功能</title>
<!-- 引入初始化 -->
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
a {
text-decoration: none;
color: #666;
}
body {
background: #fff;
color: #666;
font-size: 14px;
}
input {
outline: none;
}
.clearfix::before,
.clearfix::after {
content: '';
display: block;
clear: both;
}
.clearfix {
*zoom: 1;
}
</style>
<!-- 引入购物车样式 -->
<style>
table {
width: 800px;
margin: 0 auto;
border-collapse: collapse;
}
th {
font: normal 14px/50px '宋体';
color: #666;
}
th,
td {
border: none;
text-align: center;
border-bottom: 1px dashed #ccc;
}
input[type='checkbox'] {
width: 13px;
height: 13px;
}
tbody p {
position: relative;
bottom: 10px;
}
tbody .add,
tbody .reduce {
float: left;
width: 22px;
height: 22px;
border: 1px solid #ccc;
text-align: center;
background: none;
outline: none;
cursor: pointer;
}
tbody input[type='text'] {
width: 50px;
float: left;
height: 18px;
text-align: center;
}
tbody .count-c {
width: 98px;
margin: 0 auto;
}
button[disabled] {
color: #ddd;
cursor: not-allowed;
}
tbody tr:hover {
background: #eee;
}
tbody tr.active {
background: rgba(241, 209, 149, 0.945);
}
.controls {
width: 790px;
margin: 10px auto;
border: 1px solid #ccc;
line-height: 50px;
padding-left: 10px;
position: relative;
}
.controls .del-all,
.controls .clear {
float: left;
margin-right: 50px;
}
.controls p {
float: right;
margin-right: 100px;
}
.controls span {
color: red;
}
.controls .pay {
position: absolute;
right: 0;
width: 80px;
height: 54px;
background: red;
font: bold 20px/54px '宋体';
color: #fff;
text-align: center;
bottom: -1px;
}
.controls .total-price {
font-weight: bold;
}
</style>
</head>
<body>
<div class="car">
<table>
<thead>
<tr>
<th><input type="checkbox" id="all" />全选</th>
<th>商品</th>
<th>单价</th>
<th>商品数量</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody id="carBody">
<!-- <tr>
<th><input type="checkbox" /></th>
<th>商品名称</th>
<th>100</th>
<th>2</th>
<th>200</th>
<th>操作</th>
</tr> -->
</tbody>
</table>
<div class="controls clearfix">
<a href="javascript:" class="del-all">删除所选商品</a>
<a href="javascript:" class="clear">清理购物车</a>
<a href="javascript:" class="pay">去结算</a>
<p>
已经选中<span id="totalCount">0</span>件商品;总价:<span id="totalPrice" class="total-price">0¥</span>
</p>
</div>
</div>
<!-- 引入数据文件 -->
<script src="./data.js"></script>
<script>
let carBody = document.querySelector('#carBody')
let all = document.querySelector('#all')
let totalCount = document.querySelector('#totalCount')
let totalPrice = document.querySelector('#totalPrice')
// 渲染--封装为一个函数
function init() {
let htmlStr = ''
let count = 0,
sum = 0
data.forEach(function (value, index) {
htmlStr += `<tr>
<td><input class='sck' id='${index}' type="checkbox" ${
value.state ? 'checked' : ''
}/></td>
<td><img src='${value.img}'/> <p>${value.name}</p></td>
<td>${value.price}</td>
<td><button class='reduce' id='${index}'>-</button><input type='text' value='${
value.count
}'/> <button class='add' id='${index}'>+</button></td>
<td>${value.price * value.count}</td>
<td>操作</td>
</tr>`
if (value.state) {
count++
sum += value.price * value.count
}
})
carBody.innerHTML = htmlStr
// 设置全选复选框的选中状态
all.checked = count == data.length
totalCount.innerHTML = count
totalPrice.innerHTML = '¥' + sum
console.log(all.checked);
}
init()
// 实现数量增加,复选框的单击
carBody.addEventListener('click', function (e) {
if (e.target.className == 'add') {
data[e.target.id].count++
init()
} else if (e.target.className == 'reduce') {
data[e.target.id].count > 1 ? data[e.target.id].count-- : ''
init()
} else if (e.target.className == 'sck') {
data[e.target.id].state = !data[e.target.id].state
init()
}
})
// 全选和全不选
all.addEventListener('click', function () {
let state = all.checked
data.forEach(function (value) {
value.state = state
})
init()
})
</script>
</body>
</html>
复制代码
let data = [
{
id: 1, //任何数据都会有id号
state: true,
img: './images/01.jpg',
name: '牛奶',
price: 5,
count: 2
},
{
id: 2,
state: true,
img: './images/01.jpg',
name: '奶牛',
price: 10,
count: 5
},
{
id: 3,
state: false,
img: './images/01.jpg',
name: '酸奶',
price: 3,
count: 1
}
]
作者:Hsu_Ee
链接:juejin.cn/post/711300…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。