事件对象
是个对象,这个对象里有事件触发时的相关信息
事件绑定的回调函数的第一个参数就是事件对象 event、ev、e
元素.addEventListener('click',function(e){})
常用属性
- type 获取当前的事件类型
- clientX/clientY 获取光标相对于浏览器可见窗口左上角的位置 也可以是视口
- offsetX/offsetY 获取光标相对于当前DOM元素左上角的位置
- key 用户按下的键盘键的值
//鼠标跟随案例
<script>
// 获得元素
let img = document.querySelector('img')
// 添加鼠标进入事件
img.addEventListener('mouseenter', function (e) {
// 鼠标跟随效果
document.addEventListener('mousemove', function (e) {
img.style.left = e.clientX + 'px'
img.style.top = e.clientY + 'px'
})
})
</script>
//发布微博案例
<div class="w">
<!-- 操作的界面 -->
<div class="controls">
<img src="./images/9.6/tip.png" alt="" /><br />
<!-- maxlength 可以用来限制表单输入的内容长度 -->
<textarea placeholder="说点什么吧..." id="area" cols="30" rows="10" maxlength="200"></textarea>
<div>
<span class="useCount" id="useCount">0</span>
<span>/</span>
<span>200</span>
<button id="send">发布</button>
</div>
</div>
<!-- 微博内容列表 -->
<div class="contentList">
<ul id="list"></ul>
</div>
</div>
<script>
//获得元素
let area = document.querySelector('#area')
let send = document.querySelector('#send')
let count = document.querySelector('#useCount')
let ul = document.querySelector('#list')
// 模拟数据
let dataArr = [
{ uname: '司马懿', imgSrc: './images/9.5/01.jpg' },
{ uname: '女娲', imgSrc: './images/9.5/02.jpg' },
{ uname: '百里守约', imgSrc: './images/9.5/03.jpg' },
{ uname: '亚瑟', imgSrc: './images/9.5/04.jpg' },
{ uname: '虞姬', imgSrc: './images/9.5/05.jpg' },
{ uname: '张良', imgSrc: './images/9.5/06.jpg' },
{ uname: '安其拉', imgSrc: './images/9.5/07.jpg' },
{ uname: '李白', imgSrc: './images/9.5/08.jpg' },
{ uname: '阿珂', imgSrc: './images/9.5/09.jpg' },
{ uname: '墨子', imgSrc: './images/9.5/10.jpg' },
{ uname: '鲁班', imgSrc: './images/9.5/11.jpg' },
{ uname: '嬴政', imgSrc: './images/9.5/12.jpg' },
{ uname: '孙膑', imgSrc: './images/9.5/13.jpg' },
{ uname: '周瑜', imgSrc: './images/9.5/14.jpg' },
{ uname: '老夫子', imgSrc: './images/9.5/15.jpg' },
{ uname: '狄仁杰', imgSrc: './images/9.5/16.jpg' },
{ uname: '扁鹊', imgSrc: './images/9.5/17.jpg' },
{ uname: '马可波罗', imgSrc: './images/9.5/18.jpg' },
{ uname: '露娜', imgSrc: './images/9.5/19.jpg' },
{ uname: '孙悟空', imgSrc: './images/9.5/20.jpg' },
{ uname: '黄忠', imgSrc: './images/9.5/21.jpg' },
{ uname: '百里玄策', imgSrc: './images/9.5/22.jpg' }
]
// 创建输入事件
area.addEventListener('input', function () {
// 获得输入内容的长度显示出来
// trim()去除前后空格字符串
count.innerHTML = area.value.trim().length
})
// 封装一个函数,根据当前日期值生成 2020年12月05日 00:07:54 格式
function dateFormat() {
// new Date():可以获取当前时间
let mydate = new Date()
// 获取年:getFullYear()
let year = mydate.getFullYear()
// 获取月,月从0开始:getMonth()
let month = mydate.getMonth() + 1
month = month < 10 ? '0' + month : month
// 获取日:getDate()
let day = mydate.getDate()
day = day < 10 ? '0' + day : day
// 获取时:getHours()
let hour = mydate.getHours()
hour = hour < 10 ? '0' + hour : hour
// 获取分:getMinutes()
let minute = mydate.getMinutes()
minute = minute < 10 ? '0' + minute : minute
// 获取秒:getSeconds()
let second = mydate.getSeconds()
second = second < 10 ? '0' + second : second
return `${year}年${month}月${day} ${hour}:${minute}:${second}`
}
// 创建点击事件
send.addEventListener('click', function () {
if (area.value.trim().length == 0) {
alert('请输入内容')
return
}
let index = parseInt(Math.random() * dataArr.length)
let obj = dataArr[index]
let myli = document.createElement('li')
myli.innerHTML = `<div class="info">
<img class="userpic" src="${obj.imgSrc}" />
<span class="username">${obj.uname}</span>
<p class="send-time">发布于${dateFormat()}</p>
</div>
<div class="content">${area.value}</div>
<span class="the_del">X</span>`
ul.appendChild(myli)
// 清空输入框内容
area.value = ''
count.innerHTML = '0'
// 聚焦调用事件
area.focus()
})
area.addEventListener('keyup', function (e) {
console.log(e.keycode)
if (e.keyCode == 13) {
send.click()
}
})
事件流
事件流指的是事件完整执行过程中的流动路径
捕获阶段
从DOM的根元素开始去执行对应的事件 (从外到里)
事件冒泡概念
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
阻止事件流动的代码
事件对象.stopPropagation()
此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
mouseenter 和 mouseleave 没有冒泡效果
组织事件默认行为
e.preventDefault()
阻止默认行为,比如链接点击不跳转,表单域的不提交
事件委托
事件委托是利用事件流的特征解决一些开发需求的知识技巧
- 优点:给父级元素加事件(可以提高性能)
- 原理:事件委托其实是利用事件冒泡的特点, 给父元素添加事件,子元素可以触发
- 实现:事件对象.target 可以获得真正触发事件的元素
<!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></tbody>
</table>
<script>
// 1. 准备好数据后端的数据
let arr = [
{
stuId: 1001,
uname: '欧阳霸天',
age: 19,
gender: '男',
salary: '20000',
city: '上海'
},
{
stuId: 1002,
uname: '令狐霸天',
age: 29,
gender: '男',
salary: '30000',
city: '北京'
},
{
stuId: 1003,
uname: '诸葛霸天',
age: 39,
gender: '男',
salary: '2000',
city: '北京'
}
]
let tbody = document.querySelector('tbody')
// 2.动态渲染:将变化的时候渲染为html结构
// 渲染:根据数据生成动态结构的过程,称为渲染
// 动态:是指数据不是固定不变的,而是变化的,一般我们会从后台请求数据回客户端
for (let i = 0; i < arr.length; i++) {
// 2.1 创建一个tr
let tr = document.createElement('tr')
// 2.2 为tr添加内容
tr.innerHTML = `<td>${arr[i].stuId}</td>
<td>${arr[i].uname}</td>
<td>${arr[i].age}</td>
<td>${arr[i].gender}</td>
<td>${arr[i].salary}</td>
<td>${arr[i].city}</td>
<td>
<a href="javascript:" class='btn del' id='${arr[i].stuId}'>删除</a>
</td>`
// 2.3 将创建好的tr添加到指定的容器中
tbody.appendChild(tr)
}
// 3.实现数据的新增
// 3.1 获取元素。绑定事件
let add = document.querySelector('.add')
add.addEventListener('click', function() {
// 3.2 收集数据,生成一个数据对象
let uname = document.querySelector('.uname').value
let age = document.querySelector('.age').value
let gender = document.querySelector('.gender').value
let salary = document.querySelector('.salary').value
let city = document.querySelector('.city').value
// 如果没有数据,则赋值默认值:1001,否则取最后一个元素的id+1
let stuId = arr.length == 0 ? 1001 : arr[arr.length - 1].stuId + 1 // 获取当前数组的最后一个元素stuId + 1
let obj = {
stuId,
uname,
age,
gender,
salary,
city
}
console.log(obj)
// 额外操作:为了下一次的id的正确的获取,我们需要将当前创建的对象添加到数组中
arr.push(obj)
// 3.3 创建一个新的tr
let newtr = document.createElement('tr')
// 3.4 根据数据设置tr的内容
newtr.innerHTML = `<td>${obj.stuId}</td>
<td>${obj.uname}</td>
<td>${obj.age}</td>
<td>${obj.gender}</td>
<td>${obj.salary}</td>
<td>${obj.city}</td>
<td>
<a href="javascript:" class='del' id='${obj.stuId}'>删除</a>
</td>`
// 3.5 将新创建的tr追加到tbody中
tbody.appendChild(newtr)
})
// 4.实现数据的删除
// 4.1 为删除按钮绑定事件--事件委托:将事件绑定给父容器
tbody.addEventListener('click', function(e) {
// console.log(e)
// 默认情况下,我们单击tbody的任意位置都能触发事件,但是我们只需要单击删除按钮才真正的进行处理
// 所以我们需要判断当前触发事件的元素是否有某个指定的标识,一般可以在渲染的时候设置一个class
// 那么我们就可以通过e.target 获取当前触发事件的元素,判断这个元素是否拥有某个样式
// console.log(e.target)
// 为true说明有这个样式,说明用户单击了 删除
// contains:判断元素是否包含某个指定名称的类名样式
if (e.target.classList.contains('del')) {
console.log(e.target.id)
// 4.2 删除元素: 父容器.removeChild(直接子元素)
tbody.removeChild(e.target.parentNode.parentNode)
// 4.3 扩展操作:同时删除数组中的对应的数据
// a.因为我们要删除数组中的成员,所以我会考虑splice方法
// b.我们发现,关键的任务就是找到当前你想删除的数据的索引
// b.1 我们什么时候有索引:在循环生成这个结构的时候有索引
// b.2 我们什么时候需要用索引:在单击删除按钮的时候使用
// b.3 在删除按钮的事件中我们能够取到什么 :e.target
// arr.splice(e.target.id, 1)
let id = e.target.id
// 循环遍历数组,找到id对应的数据的索引
for (let i = 0; i < arr.length; i++) {
if (arr[i].stuId == id) {
// 就是你,要被删除的
arr.splice(i, 1)
break
}
}
console.log(arr)
}
})
</script>
</body>
</html>