事件高级
看完本节内容,你需要做到 |
---|
能够写出元素注册事件的两种方式 |
能够说出删除事件的两种方式 |
能够说出DOM事件流的三个阶段 |
能够利用事件对象完成跟随鼠标案例 |
能够封装阻止冒泡的兼容性函数 |
能够说出事件委托的原理 |
能够说出常用的鼠标和键盘事件 |
1.注册事件(绑定事件)
给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式:传统方式和方法监听注册方式
1.1 传统方式和方法监听注册方式的区别
1.2 addEventListener 事件监听方式(IE9之后)
eventTarge.addEventListener(type,listener[,useCapture])
新建.html文件,执行代码如下
<!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>
</head>
<body>
<button>传统注册事件</button>
<button>方法监听注册事件</button>
<script>
// 1.传统方式注册事件
var btns = document.querySelectorAll('button');
btns[0].onclick = function() {
alert('这是传统的注册事件1!');
}
btns[0].onclick = function() {
alert('这是传统的注册事件2!');
}
// 当鼠标点击后,后面的处理事件把前面的就会覆盖,鼠标点击按钮1时,只会出现'这是传统的注册事件2!'的弹窗,体现出唯一性。
// 2.事件监听注册事件
// (1) addEventListene 里面的事件类型是字符串 必定要加引号,而且不带on
// (2) 同一个元素,同一个事件可以添加多个侦听器(事件处理程序)
btns[1].addEventListener('click',function() {
alert('这是方法监听注册事件1!')
})
btns[1].addEventListener('click',function() {
alert('这是方法监听注册事件2!')
})
btns[1].addEventListener('click',function() {
alert('这是方法监听注册事件3!')
})
// 当鼠标点击后,会依次弹出弹窗
</script>
</body>
</html>
效果如下
1.3 attachEvent 事件监听方式(IE9之前)
上面的addEventListener方法为IE9之后的事件监听注册方法。IE9之前用attachEvent方法作为事件监听注册方法,非标,不推荐
语法:
eventTarge.attachEvent(eventNameWithOn,callback)
新建.html文件,执行代码如下
<!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>
</head>
<body>
<button>ie9 attachEvent</button>
<script>
// 1.attachEvent ie9之前的版本支持
var btn = document.querySelector('button');
btn.attachEvent('onclick',function() {
alert('支持ie9之前的版本');
})
</script>
</body>
</html>
效果如下
1.4 注册事件兼容性解决方案
总结:addEventListener方法可以解决大多数情况,解决不掉的,直接用传统方式;attachEvent方法了解即可。
2.删除事件(解绑事件)
2.1 删除事件的方式
新建.html文件,执行代码如下
<!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>
div {
width: 100px;
height: 35px;
background-color: rgb(42, 205, 210);
}
</style>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
// 1.需求:第一次点击盒子,出现弹窗,第二次点击盒子,不再出现弹窗了
var divs = document.querySelectorAll('div');
// (1) 用传统事件来做
divs[0].onclick = function() {
alert('用的是传统事件!');
divs[0].onclick = null;
}
// (2) 用addEventListener方法监听事件 ie9之后用
divs[1].addEventListener('click',fn); // 里面的fn指fn()的调用,不需要加小括号
function fn() {
alert('用的是事件监听方法');
divs[1].removeEventListener('click',fn)
}
// (3) 用attachEvent方法监听事件 ie9之前用
// divs[2].attachEvent('onclick',fn1); // 里面的fn指fn()的调用,不需要加小括号
// function fn1() {
// alert('33333');
// divs[2].detachEvent('onclick',fn1);
// }
</script>
</body>
</html>
效果如下
2.2 删除事件兼容性解决方案
3.DOM事件流
3.1 理论部分
总结:
事件的传播顺序,我们称为事件流,如果从上往下依次传播事件的过程,我们称为事件捕获阶段,到底之后会处于目标阶段,再从下往上将这个事件传播上去,这个过程称为事件冒泡阶段。
3.2 实践部分
新建.html文件,执行代码如下
<!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 {
width: 200px;
height: 200px;
margin: 200px;
background-color: rgb(109, 209, 46);
}
.son {
width: 100px;
height: 100px;
margin: 50px;
background-color: rgb(186, 34, 34);
}
</style>
</head>
<body>
<!-- .father>.son -->
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// dom 事件流 三个阶段
// 1.JS代码中只能执行捕获或者冒泡其中的一个阶段。
// 2.onclick 和 attachEvent (ie) 只能得到冒泡阶段。
// 3. 捕获阶段 如果addEventListener 第三个参数是 true, 那么则处于捕获阶段
// 执行顺序:document -> html -> body -> father -> son
// var son = document.querySelector('.son');
// son.addEventListener('click',function() {
// alert('son后弹出');
// },true);
// var father = document.querySelector('.father');
// father.addEventListener('click',function() {
// alert('father先弹出');
// },true)
// 4 冒泡阶段 如果addEventListener 第三个参数是false或者省略, 那么则处于冒泡阶段
// 执行顺序:son -> fahter -> body -> html -> document
var son = document.querySelector('.son');
son.addEventListener('click',function() {
alert('son先弹出');
},false);
var father = document.querySelector('.father');
father.addEventListener('click',function() {
alert('father后弹出');
})
document.addEventListener('click',function() {
alert('document最后弹出');
})
</script>
</body>
</html>
效果如下
4.事件对象
4.1 事件对象简介
新建.html文件,执行代码如下
<!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>
</head>
<body>
<div>123</div>
<script>
// 事件对象
// (1) 用传统方式
var div = document.querySelector('div');
div.onclick = function(e) {
// console.log(event);
// console.log(window.event);
// 考虑兼容性问题,用下行代码 && 和 || 是逻辑运算符
// e = e || window.event;
console.log(e);
}
// (2) 用方法监听注册方式
// div.addEventListener('click',function(event) {
// console.log(event);
// })
// 1.event 就是一个事件对象,写到我们侦听函数的 小括号里面 当形参来看
// 2.事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
// 3.事件对象 是我们事件的一系列相关数据的集合 跟事件相关的
// 比如:鼠标点击里面就包含了鼠标的相关信息,例如鼠标坐标等等
// 比如:如果是键盘事件里面就包含的键盘事件的信息 例如判断用户按下了哪个键
// 4.这个事件对象我们可以自己命名 比如event、evt、e
// 5.事件对象也有兼容性的问题 ie的678通过window.event来获取事件对象
// 兼容性写法 e = e || window.event;
</script>
</body>
</html>
效果如下
效果如下
4.2 事件对象的常见属性和方法
新建.html文件,执行代码如下
<!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>
div {
width: 60px;
height: 50px;
background-color: rgb(64, 161, 127);
}
</style>
</head>
<body>
<div>123</div>
<span>456</span>
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
// 常见事件对象的属性和方法
// (1) e.target 返回的触发事件的对象(元素) this 返回的是绑定事件的对象(元素)
var div = document.querySelector('div');
div.addEventListener('click',function(e) {
console.log(e.target);
console.log(this);
})
var ul = document.querySelector('ul');
ul.addEventListener('click',function(e) {
// 我们给ul绑定了事件 那么this 就指向ul;点击的li,真正的是li触发了这个点击事件,因为有冒泡,所以触发
console.log(this);
// e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
console.log(e.target);
})
// 区别:
// e.target 点击了那个元素,就返回那个元素
// this 那个元素绑定了这个点击事件,那么就返回谁
// (1) 考虑兼容性时,可以如下写法
var span = document.querySelector('span');
span.onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
console.log(target);
}
// (2) 了解一下,跟this有个非常相似的属性 currentTarget 但是ie浏览器的678版本不认识
</script>
</body>
</html>
效果如下
4.3 阻止默认行为
新建.html文件,执行下面代码
<!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>
div {
width: 200px;
height: 100px;
background-color: rgb(174, 72, 162);
}
</style>
</head>
<body>
<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub">
</form>
<script>
// 常见事件对象的属性和方法
// (1) e.type返回事件类型
var div = document.querySelector('div');
// 点击
div.addEventListener('click',fn); // 不写第三个参数,冒泡阶段调用事件处理程序
// 鼠标经过
div.addEventListener('mouseover',fn);
// 鼠标离开
div.addEventListener('mouseout',fn);
function fn(e) {
console.log(e.type);
}
// (2) 阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交
// (2.1) e.preventDefault
var a = addEventListener('click',function(e) {
e.preventDefault(); // dom推荐的标准写法 ie不支持
})
// (2.2) 传统的注册方式
a.onclick = function(e) {
// (2.2.1) 普通浏览器 e.preventDefault(); 方法
e.preventDefault();
// (2.2.2) 适用于低版本浏览器 ie678 returnValue 属性
e.returnValue;
// (2.2.3) 可以利用return false 也能阻止默认行为 没有兼容性问题
// 特点:return 后面的代码不执行了,而且该方式只限于传统的注册方式
return false;
alert(11); // 不会弹出,因为return后面的代码不会执行
}
</script>
</body>
</html>
效果如下
5.阻止事件冒泡
5.1 阻止事件冒泡的两种方式
[提示]:事件冒泡的坏处
(在实际开发中,捕获阶段不太关系,我们更关心的是冒泡阶段)
复用**1.2 addEventListener 事件监听方式(IE9之后)**的代码,执行代码如下
<!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 {
width: 200px;
height: 200px;
margin: 200px;
background-color: rgb(109, 209, 46);
}
.son {
width: 100px;
height: 100px;
margin: 50px;
background-color: rgb(186, 34, 34);
}
</style>
</head>
<body>
<!-- .father>.son -->
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
var son = document.querySelector('.son');
son.addEventListener('click',function(e) {
alert('son先弹出');
e.stopPropagation(); // stop 停止 Propagation 传播
// 若考虑兼容性问题时
// e.cancelBubble = true; // cancel 取消 bubble 泡泡
},false);
var father = document.querySelector('.father');
father.addEventListener('click',function() {
alert('father后弹出');
})
document.addEventListener('click',function() {
alert('document最后弹出');
})
</script>
</body>
</html>
效果如下
6.事件委托(代理、委派)
[提示]:事件冒泡的好处
原理
新建.html文件,执行代码如下
<!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>
</head>
<body>
<!-- ul>li*5 -->
<ul>
<!-- shift+alt+a -->
<li>今天务必早点睡</li>
<li>今天务必早点睡</li>
<li>今天务必早点睡</li>
<li>今天务必早点睡</li>
<li>今天务必早点睡</li>
</ul>
<script>
// 事件委派的核心原理:给父节点添加侦听器,利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('click',function(e) {
// alert('今晚务必早点睡');
// e.target 这个可以得到我们点击的对象
e.target.style.backgroundColor = 'green';
})
</script>
</body>
</html>
效果如下
7 常用的鼠标事件
7.1 常用的鼠标事件
新建.html文件,执行代码如下
<!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>
</head>
<body>
我无法右键点击复制
<script>
// (1) contextmenu 我们可以禁用右键菜单
document.addEventListener('contextmenu',function(e) {
e.preventDefault();
})
// (2) selectstart 禁止选中文字
document.addEventListener('selectstart',function(e) {
e.preventDefault();
})
</script>
</body>
</html>
效果如下
7.2 鼠标事件对象
提示:判断鼠标点击了哪个位置
新建.html文件,执行代码如下
<!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>
body {
height: 3000px;
}
</style>
</head>
<body>
<script>
// 鼠标点击对象 MouseEvent
document.addEventListener('click',function(e) {
console.log(e);
// (1) client 鼠标在可视区的x和y的坐标
console.log(e.clientX);
console.log(e.clientY);
// (2) page 鼠标在页面文档的x和y坐标
console.log(e.pageX);
console.log(e.pageY);
// (3) sreen 鼠标相对于电脑屏幕的x和y坐标
// e.screenX;
// e.screenY;
})
</script>
</body>
</html>
效果如下
7.3 案例:跟随鼠标移动的天数
案例:找一张图片,这个图片一直跟随鼠标移动
案例分析
新建.html文件,执行代码如下
<!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>
/* 图片改成绝对定位 绝对定位的特点之三:绝对定位不再占用原先的位置。(脱标)*/
img {
position: absolute;
width: 100px;
height: 80px;
}
</style>
</head>
<body>
<img src="image/001.jpg" alt="">
<script>
var pic = document.querySelector('img');
// 只要一移动,就会触发事件
document.addEventListener('mousemove',function(e) {
// (1) mousemove 只要我们鼠标移动1px, 就会触发这个事件
console.log(11);
// (2) 核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标,把这个x和y坐标作为图片的top和left值
// 就可以移动图片
var x = e.pageX;
var y = e.pageY;
console.log('x坐标是' + x,'y坐标是' + y);
// (3) 千万不用忘记给left和top添加px 单位
pic.style.left = x + 'px';
pic.style.top = y + 'px';
// 鼠标和图片居中对齐 要根据图片的大小进行判断
// pic.style.left = x -50 + 'px';
// pic.style.top = y - 40 + 'px';
})
</script>
</body>
</html>
效果如下
8. 常用的键盘事件
8.1 常用的键盘事件
事件除了使用鼠标触发,还可以使用键盘触发。
新建.html文件,执行代码如下
<!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>
</head>
<body>
<script>
// 常用的键盘事件
// (1) keyup 按键弹起的时候触发
// (1.1) 传统方式
// document.onkeyup = function() {
// console.log('我弹起了');
// }
// (1.2) 监听注册方式
document.addEventListener('keyup',function() {
console.log('我弹起来了');
})
// (2) keydown 按键按下的时候触发 能识别功能键,比如:ctrl shift 左右箭头等
document.addEventListener('keydown',function() {
console.log('我按下了down');
})
// (3) keypress 按钮按下的时候触发 不能识别功能键 比如:ctrl shift 左右箭头等
document.addEventListener('keypress',function() {
console.log('我按下了press');
})
// (2) 和(3)代码交互位置,也不会影响输出结果,因为三个事件的执行顺序 keydown -> keypress -> keyup
</script>
</body>
</html>
效果如下
8.2 键盘事件对象
提示:判断键盘按下了哪个键
ASCⅡ码表
新建.html文件,执行代码如下
<!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>
</head>
<body>
<script>
// 键盘事件对象中的keyCode属性可以得到相应键的ASCⅡ码值
// (1) 我们的keyup和keydown事件不区分字母大小写 a 和 A 得到的都是 65
// (2) 我们的keypress 事件区分字母大小写 a 97 和 A 65
// document.addEventListener('keyup',function(e) {
// // console.log(e);
// console.log('up' + e.keyCode);
// })
// document.addEventListener('keypress',function(e) {
// console.log('press' + e.keyCode);
// })
// 可以利用keyCode返回的ASCⅡ码值来判断用户按下了哪个键
document.addEventListener('keyup',function(e) {
if (e.keyCode === 65) {
alert('您按下了a键');
} else {
alert('您没有按下a键')
}
})
</script>
</body>
</html>
效果如下
8.3 案例:模拟京东按键输入内容
案例:当我们按下s键,光标就会定位到搜索框
新建.html文件,执行代码如下:
<!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>
</head>
<body>
<input type="text">
<script>
var search = document.querySelector('input');
document.addEventListener('keyup',function(e) {
// 先获取s键的ASCⅡ码
// console.log(e.keyCode); // 83
if (e.keyCode === 83) {
search.focus();
}
})
</script>
</body>
</html>
效果如下
8.4 案例:模拟京东快递单号查询
要求:当我们在文本框输入内容时,文本框上面自动显示大字号的内容。
案例分析
新建.html文件,执行代码如下
<!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;
}
.search {
position: relative;
width: 178px;
margin: 100px;
}
.con {
display: none;
position: absolute;
top: -40px;
width: 171px;
border: 1px solid rgba(0, 0, 0, .2);
box-shadow: 0 2px 4px rgba(0, 0, 0, .2);
padding: 5px 0;
font-size: 18px;
line-height: 20px;
color: #333;
}
.con::before {
content: '';
width: 0;
height: 0;
position: absolute;
top: 28px;
left: 18px;
border: 8px solid #000;
border-style: solid dashed dashed;
border-color: #fff transparent transparent;
}
</style>
</head>
<body>
<div class="search">
<div class="con"></div>
<input type="text" placeholder="请输入你的快递单号" class="jd">
</div>
<script>
//快递单号输入内容时,上面的大号字体盒子(con)显示(这里面的字号更大)
//表单检测用户输入,给表单添加键盘事件
//同时把快递单号里面的值(value)获取过来赋值给con盒子(innerText)作为内容
//如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
var con = document.querySelector('.con');
var jd_input = document.querySelector('.jd');
// 表单里的值是通过value获取的,而div只能通过innerText或者innerHTML获取
jd_input.addEventListener('keyup', function() {
if (this.value == '') {
con.style.display = 'none';
} else {
con.style.display = 'block';
con.innerText = this.value;
}
})
//失去焦点,隐藏con盒子
jd_input.addEventListener('blur', function() {
con.style.display = 'none';
})
//获得焦点,显示con盒子
jd_input.addEventListener('focus', function() {
if (this.value !== '') {
con.style.display = 'block';
}
})
</script>
</body>
</html>
效果如下