1、事件对象常用属性
- e.clientX/clientY 参照视口左上角 获取光标相对于浏览器可见窗口左上角的位置 e.offsetX/offsetY 参照元素左上角的坐标值 获取光标相对于当前DOM元素左上角的位置
- e.screenX/e.screenY 鼠标单击位置参照屏幕左上角的坐标值
- keyup 获取的是当前这一次的value keydown 获取的value是上一次的value 现在不提倡使用keyCode
- e.which键码
- e.target当前真正触发事件的元素对象
跟随鼠标案例
<img src="./images/tianshi.gif" alt="" />
<script>
let img = document.querySelector('img')
// 鼠标跟随:你得监听到鼠标的移动:mousemove,绑定为文档 / body
document.addEventListener('mousemove', function(e) {
// document.body.addEventListener('mousemove', function() {
console.log(e.clientX, e.clientY)
img.style.left = e.clientX - img.offsetWidth / 2 + 'px'
img.style.top = e.clientY - img.offsetHeight / 2 + 'px'
})
2、事件流
-
事件流
- 过程: 事件捕获 => 事件目标阶段 => 事件冒泡 整个完整的过程就是事件流
- 实际工作都是使用事件冒泡为主
-
事件捕获
-
概念: 从DOM的根元素开始去执行对应的事件 (从外到里)
-
捕获机制必须使用事件监听的形式注册
-
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
- 第三个参数为true:捕获阶段触发事件
- false代表冒泡阶段触发事件-默认值
-
-
-
事件冒泡
-
概念: 当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
-
事件冒泡的必要性
-
如果没有冒泡.给大盒子注册点击事件, 点击的是里面的小盒子,会导致大盒子的点击无法执行
-
事件委托
- 自己不注册事件,将对应的事件注册给祖先元素
- 减少事件的注册,提高效率
- e.target => 当前点击的那个元素
-
-
阻止事件冒泡
-
- 先要明确那一块区域不能冒泡
-
- 需要阻止什么事件传递就给这块区域的最大盒子注册该事件
-
- 在事件处理函数里面接受事件对象, 并添加上e.stopPropagation()
-
-
-
阻止冒泡和默认行为
-
e.stopPropagation()
- 阻止事件冒泡
-
e.preventDefault()
- 链接的跳转
- 表单域跳转
-
-
事件注册的两种方式区别
-
传统on注册
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖偶就可以实现事件的解绑
- 都是冒泡阶段执行的
-
事件监听注册
-
语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
-
后面注册的事件不会覆盖前面注册的事件(同一个事件)
-
可以通过第三个参数去确定是在冒泡或者捕获阶段执行
-
必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
- 匿名函数无法被解绑
-
-
-
mouseover和 mouseenter的区别
3、事件委托
-
优点
- 减少事件的注册,只需要注册化父容器
- 可以为已存在和未来的元素注册事件,注册一次,现在未来通用
-
原理:
- 利用冒泡
-
使用场景:动态的渲染的元素的事件注册使用委托,动态渲染的元素的事件只能使用委托
let box = document.querySelector('.box')
let button = document.querySelector('button')
button.addEventListener('click', function() {
let newdiv = document.createElement('div')
newdiv.classList.add('other')
box.appendChild(newdiv)
})
// 如果为父容器绑定事件,那么所有的子元素默认情况下都能触发这个事件,不管这个子元素是 已存在,还是将来创建的
// 这里是复用事件冒泡的原理,为父容器绑定事件,将来所有子元素都会事件冒泡给这个父容器
// 这种将事件绑定给父容器的做法让子元素也能响应事件--就叫做事件委托
box.addEventListener('click', function(e) {
// e.target:获取当前真正触发事件的对象,说白了,当前用户操作那个元素,e.target就是这个元素
// console.log(e.target.classList.contains('son'))
// 我们如何判断当前元素是我们需要的元素呢?
// 1.我们会为元素添加标识,例如为元素添加一个自定义的类样式
// 2.判断当前元素是否拥有指定的标识,如果有就是我们想要的元素
if ((e.target.className = 'son')) {
console.log('单击了son')
} else if (e.target.classList.contains('sister')) {
console.log('单击了sister')
} else if (e.target.classList.contains('other')) {
console.log('单击了other')
}
})
渲染学生信息案例
需求:点击录入按钮,可以增加学生信息
业务拆分
-
查询(动态渲染)操作第一个完成
-
静态结构
-
数据
-
现在实现方式1:遍历数据,拼接html结构,最终将结果赋值给某个容器
- innerHTML
-
方式2:创建元素,设置内容,追加到指定的容器中
- createElement
- appendChild
-
-
添加
-
最关键的准备你要添加的数据,我们的核心任务是收集用户信息,准备好完整的数据
-
stuId:
- 获取数组中最后一个元素对象的stuId值 +1
- 如果数组为空,stuId 为1001
-
uname:表单元素
-
age: 表单元素
-
gender: 表单元素
-
salary:表单元素
-
city: 表单元素
-
-
添加的页面效果是:在表格中多出一行新增的数据
-
方式1:
- 根据数据创建一个新行,追加到tbody中
-
方式2
-
将生成的数据对象,追加到数组中,重新渲染
- 追加数据到数组
- 将渲染过程封装为函数
- 调用函数
-
-
-
-
删除
-
删除一定体现到数据
-
删除按钮的事件绑定应该要使用委托
-
删除数组中的指定位置的元素
- arr.splice(索引,1)
-
重新渲染
-
-
经验
-
如果一个业务需要某个值才能进行,我们一般有两种方式来处理
-
如果可以传递参数就传递参数
-
先存储再获取
-
存储在那里
- 如何取值方便来决定
-
存储什么
- 基于业务需求
-
如何获取
-
-
-
-
<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>
// 获取元素
let tbody = document.querySelector('tbody')
let add = document.querySelector('.add')
// 获取表单元素的值-value值
let unameEle = document.querySelector('.uname')
let ageEle = document.querySelector('.age')
let genderEle = document.querySelector('.gender')
let salaryEle = document.querySelector('.salary')
let cityEle = document.querySelector('.city')
// 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: '北京'
}
]
// 2.实现动态渲染 静态结构+数据
// 遍历数据 拼接字符串 替换数据 填充到指定位置
function init() {
let htmlStr = ''
arr.forEach(function (item, index) {
// 把要输入的内容拼接到字符串
htmlStr += `<tr>
<td>${index + 1}</td>
<td>${item.uname}</td>
<td>${item.age}</td>
<td>${item.gender}</td>
<td>${item.salary}</td>
<td>${item.city}</td>
<td>
<a href="javascript:" class="del" id='${item.stuId}'>删除</a>
</td>
</ tr>`
})
tbody.innerHTML = htmlStr
}
init()
// 实现数据的新增
// 1.准备好需要新增的数据,添加到数据源
// 2.渲染
add.addEventListener('click', function () {
// 收集数据,生成一个对象
let obj = {
// 获取最后一个数据对象stuId+1,如果没有数据就给ID赋值为1001
stuId: arr.length > 0 ? arr[arr.length - 1].stuId + 1 : 1001,
uname: unameEle.value,
age: ageEle.value,
gender: genderEle.value,
salary: salaryEle.value,
city: cityEle.value
}
// 将生成的对象添加给数组
arr.push(obj)
// 渲染
init()
})
// 实现数据的删除-动态渲染的元素(新增的元素,未来的元素)的事件的绑定需要使用事件委托
tbody.addEventListener('click', function (e) {
// e.target当前用户点击的元素
// 判断是否点击删除处理
if (e.target.className == 'del') {
// 获取你想删除的数据id
let id = e.target.id
console.log(id);
// 根据id找到对应的索引
for (let i = 0; i < arr.length; i++) {
// 删除数据
if (id == arr[i].stuId{
arr.splice(i, 1)
init()
}
}
}
})
购物车案例
<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"></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')
// 数据动态渲染: 数据 +结构 : 遍历 + 拼接 + 赋值
function init() {
console.log(data)
let htmlStr = ''
data.forEach(function(item, index) {
htmlStr += `<tr>
<td>
<input class="s_ck" type="checkbox" ${item.state ? 'checked' : '' }/>
</td>
<td>
<img src="${item.img}" />
<p>${item.name}</p>
</td>
<td class="price">${item.price}</td>
<td>
<div class="count-c clearfix">
<button class="reduce" id='${index}'>-</button>
<inputtype="text"value="${item.count}" />
<button class="add" id='${index}'>+</button>
</div>
</td>
<td class="total">${item.price * item.count}¥</td>
<td>
<a href="javascript:" class="del" id='${
item.id
}'>删除</a>
</td>
</tr>`
})
carBody.innerHTML = htmlStr
}
init()
// 加减是动态生成的元素,事件绑定需要使用事件委托
carBody.addEventListener('click', function(e) {
if (e.target.className == 'add') {
// 加
let index = e.target.id
// 修改数据
data[index].count++
// 重新渲染
init()
} else if (e.target.className == 'reduce') {
// 减
let index = e.target.id
if (data[index].count == 1) {
return
}
// 修改数据
data[index].count--
// 重新渲染
init()
} else if (e.target.className == 'del') {
// 删除
let id = e.target.id
// 直接删除
data = data.filter(function(v) {
return v.id != id
})
init()
}
})