🌈个人主页:前端青山 🔥系列专栏:JavaScript篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript-事件传播
目录
事件 传播
阻止事件传播
默认行为
阻止浏览器默认行为
事件委托
target
封装事件库
事件 传播
- 浏览器内的事件流机制
什么是事件的执行机制呢?
- 思考一个问题?
- 当一个大盒子嵌套一个小盒子的时候,并且两个盒子都有点击事件
- 你点击里面的小盒子,外面的大盒子上的点击事件要不要执行
- 当元素触发一个事件的时候,其父元素也会触发相同的事件,父元素的父元素也会触发相同的事件
- 事件捕获 / 目标 / 冒泡
- 事件捕获 : 从上到下、从祖先到子孙依次传递事件的过程
- 事件目标 : 触发事件的对象,你是点击在哪个元素身上了,那么这个事件的 目标 就是什么
- 事件冒泡 : 从下到上、从子孙到祖先依次传递事件的过程
- 浏览器默认启动了事件冒泡!!!
- IE和欧朋浏览器不支持事件捕获。
<!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{
border: 1px solid black;
}
#box{
width: 200px;
height: 150px;
background: pink;
}
.pox{
width: 100px;
height: 100px;
background: yellow;
}
</style>
</head>
<body>
<div id="box">
<div class="pox"></div>
</div>
<script>
//1. html
var html = document.documentElement;
//2. body
var body = document.body;
//3. box
var box = document.querySelector('#box');
//4. pox
var pox = document.querySelector('.pox');
document.onclick = function(){
alert('document');
}
html.onclick = function(){
alert('html');
}
body.onclick = function(){
alert('body');
}
box.onclick = function(){
alert('box');
}
pox.onclick = function(evt){
var e = evt || window.event;
//标准浏览器:阻止事件冒泡
// e.stopPropagation();
//IE浏览器:阻止事件冒泡
// e.cancelBubble = true;
//兼容
e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
alert('pox');
}
</script>
</body>
</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{
border: 1px solid black;
}
#box{
width: 200px;
height: 150px;
background: pink;
}
.pox{
width: 100px;
height: 100px;
background: yellow;
}
</style>
</head>
<body>
<div id="box">
<div class="pox"></div>
</div>
<script>
//1. html
var html = document.documentElement;
//2. body
var body = document.body;
//3. box
var box = document.querySelector('#box');
//4. pox
var pox = document.querySelector('.pox');
document.onclick = function(){
alert('document');
}
html.onclick = function(){
alert('html');
}
body.onclick = function(){
alert('body');
}
box.onclick = function(){
alert('box');
}
pox.onclick = function(evt){
var e = evt || window.event;
//标准浏览器:阻止事件冒泡
// e.stopPropagation();
//IE浏览器:阻止事件冒泡
// e.cancelBubble = true;
//兼容
e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
alert('pox');
}
</script>
</body>
</html>
标准浏览器: event.stopPropagation() IE浏览器: event.cancelBubble = true; 兼容:
//阻止事件冒泡的兼容
function stopPropagation(evt){
var e = evt || window.event;
e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
}
默认行为
- 默认行为,就是不用我们注册,它自己就存在的事情
- 比如我们点击鼠标右键的时候,会自动弹出一个菜单
- 比如我们点击 a 标签的时候,我们不需要注册点击事件,他自己就会跳转页面
- ...
- 这些不需要我们注册就能实现的事情,我们叫做 默认事件
阻止浏览器默认行为
标准浏览器: event.preventDefault() IE浏览器: event.returnValue = false 兼容:
//阻止浏览器默认行为的兼容
function preventDefault(evt){
var e = evt || window.event;
e.preventDefault ? e.preventDefault() : e.returnValue = false;
}
return false : 既阻止默认行为,也阻止事件冒泡!
<script>
//获取ul
var ul = document.querySelector('ul');
//右键菜单事件 oncontextmenu
document.oncontextmenu = function(evt){
var e = evt || window.event;
//标准浏览器:阻止默认行为
// e.preventDefault();
//IE浏览器:阻止默认行为
// e.returnValue = false;
//兼容
e.preventDefault ? e.preventDefault() : e.returnValue = false;
// alert('右键菜单已禁用!');
ul.style.display = 'block';
ul.style.left = e.pageX + 'px';
ul.style.top = e.pageY + 'px';
}
document.onclick = function(){
ul.style.display = 'none';
}
</script>
<script>
//获取a
var a = document.querySelector('a');
//点击事件
a.onclick = function(event){
// e = event || window.event;
// e.preventDefault ? e.preventDefault() : e.returnValue = false;
return false; // 既阻止默认行为,也阻止事件冒泡
}
</script>
事件委托
- 就是把我要做的事情委托给别人来做
- 因为我们的冒泡机制,点击子元素的时候,也会同步触发父元素的相同事件
- 所以我们就可以把子元素的事件委托给父元素来做
将加到子元素上的事件,添加到父元素上,为了提高性能。原理是利用了事件冒泡。
- 实现事件委托
<!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>1</li>
<li>2</li>
<li>3</li>
<h4>hhhhhhhhhhhh</h4>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<p>pppppppppppp</p>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
// //获取所有的li
// var li = document.querySelectorAll('ul>li');
// //遍历,添加事件
// for(var i = 0,len = li.length;i < len;i ++){
// li[i].onclick = function(){
// alert(this.innerText);
// }
// }
//获取ul
var ul = document.querySelector('ul');
//添加事件
ul.onclick = function(evt){
var e = evt || window.event;
//获取事件源
//标准浏览器
// var target = e.target;
//IE浏览器
// var target = e.srcElement;
var target = e.target || e.srcElement;
//过滤
if(target.nodeName === 'LI'){
alert(target.innerText);
}
// alert(this.innerText);
}
var o_li = document.createElement('li');
o_li.innerText = 11;
ul.appendChild(o_li);
</script>
</body>
</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>1</li>
<li>2</li>
<li>3</li>
<h4>hhhhhhhhhhhh</h4>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<p>pppppppppppp</p>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
// 已知页面上有结构ul,内有10个li,每个li的内容不同,请使用事件委托的方式给每个li都绑定点击事件,点击的时候打印对应li的内容
//获取ul
var ul = document.querySelector('ul');
//添加事件
ul.onclick = function(evt){
var e = evt || window.event;
//获取事件源
//标准浏览器
// var target = e.target;
//IE浏览器
// var target = e.srcElement;
var target = e.target || e.srcElement;
//过滤
if(target.nodeName === 'LI'){
alert(target.innerText);
}
// alert(this.innerText);
}
var o_li = document.createElement('li');
o_li.innerText = 11;
ul.appendChild(o_li);
</script>
</body>
</html>
target
- target 这个属性是事件对象里面的属性,表示你点击的目标
- 当你触发点击事件的时候,你点击在哪个元素上,target 就是哪个元素
- 这个 target 也不兼容,在 IE 下要使用 srcElement
标准浏览器: event.target IE浏览器: event.srcElement
// 获取事件源的兼容
function getTarget(evt){
var e = evt || window.event;
return e.target || e.srcElement;
}
封装事件库
<!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>
#box{
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
//callBack : 回调
function bindEvent(dom,tapCB,clickCB){
//记录触摸开始的时间
var startTime = 0;
//是否移动了手指
var isMove = false;
//监听触摸开始事件
dom.addEventListener('touchstart',function(){
//当前时间
startTime = Date.now();
})
dom.addEventListener('touchmove',function(){
isMove = true;
})
dom.addEventListener('touchend',function(){
//如果触摸的时间差 <= 150 && 移动了
if(Date.now() - startTime <= 150 && isMove){
//轻触
if(tapCB instanceof Function){
tapCB();
}
}else{
//点击
if(typeof clickCB === 'function'){
clickCB();
}
}
})
}
//获取div
var div = document.querySelector('#box');
bindEvent(div,function(){
console.log('我摸了一下');
},function(){
console.log('我点击了一个div');
})
</script>
</body>
</html>
- 移动端tap事件
// 封装事件
// dom : 事件源,触发事件的对象
// tapCallback : 轻击后的回调函数,轻击后想要执行什么?
// clickCallback : 点击后的回调函数,点击后想要执行什么?
var bindTapEvent = function(dom, tapCallback, clickCallback) {
// 声明变量-开始时间
var startTime = 0;
// 声明变量-记录是否移动-默认为false,没有移动
var isMove = false;
//监听触摸开始事件
dom.addEventListener('touchstart', function(e) {
//记录触摸后的时间
startTime = Date.now()
});
//监听触摸移动事件
dom.addEventListener('touchmove', function(e) {
//移动后,记录为true
isMove = true
});
//监听触摸结束事件
dom.addEventListener('touchend', function(e) {
//检测触摸时间 与 是否移动
if ((Date.now() - startTime) < 150 && isMove) {
// 假设点击的时间间隔小于150ms为轻击事件
tapCallback && tapCallback.call(this, e)
} else {
// 假设点击的时间间隔大于150ms为点击事件
clickCallback && clickCallback.call(this, e)
}
//开始时间恢复为0
startTime = 0;
//记录移动为false
isMove = false;
});
}
- 移动端左滑右滑事件
/*
Touch事件:
touches:当前位于屏幕上的所有手指的一个列表
targetTouches:位于当前DOM元素上的手指的一个列表;
changedTouches:涉及当前事件的手指的一个列表;
screenX,screenY:触摸点相对于屏幕上边缘的坐标;
clientX,clientY:触摸点相对于浏览器的viewport左边缘的坐标,不包括左边的滚动距离;
pageX,pageY:触摸点相对于document的左边缘的坐标,与clientX不同的是它包括左边滚动的距离,如果有的话;
target:总是表示手指最开始放在触摸设备上的触发点所在位置的element。
*/
/**
* 用touch事件模拟点击、左滑、右滑、上拉、下拉等事件,
* 是利用touchstart和touchend两个事件发生的位置来确定是什么操作。
* 例如:
* 1、touchstart和touchend两个事件的位置基本一致,也就是没发生位移,那么可以确定用户是想点击按钮等。
* 2、touchend在touchstart正左侧,说明用户是向左滑动的。
* 利用上面的原理,可以模拟移动端的各类事件。
**/
var EventUtil = (function() {
//支持事件列表(左滑、右滑)
var eventArr = ['eventswipeleft', 'eventswiperight'
];
//touchstart事件,delta记录开始触摸位置
function touchStart(event) {
//声明空对象,用来记录触摸开始时的位置和时间信息
this.delta = {};
//添加x坐标值
this.delta.x = event.touches[0].pageX;
//添加y坐标值
this.delta.y = event.touches[0].pageY;
}
/**
* touchend事件,计算两个事件之间的位移量
* 1、如果位移量很小或没有位移,看做点击事件
* 2、如果位移量较大,x大于y,可以看做平移,x>0,向右滑,反之向左滑。
* 这样就模拟的移动端几个常见的时间。
* */
function touchEnd(event) {
//记录开始时的位置时间信息
var delta = this.delta;
//删除开始时记录的信息
delete this.delta;
//计算坐标差值
delta.x -= event.changedTouches[0].pageX;
delta.y -= event.changedTouches[0].pageY;
// 左右滑动
if (Math.abs(delta.x) > Math.abs(delta.y)) {
//左滑
if (delta.x > 0) {
this['eventswipeleft'].map(function(fn) {
fn(event);
});
} else { //右滑
this['eventswiperight'].map(function(fn) {
fn(event);
});
}
}
}
//绑定事件
function bindEvent(dom, type, callback) {
if (!dom) { //如果没有节点对象,则抛出错误
console.error('dom is null or undefined');
}
//遍历数组,检测节点对象是否绑定过事件
var flag = eventArr.some(function(key){
return dom[key]
});
//未绑定过事件
if (!flag) {
//进行绑定事件
dom.addEventListener('touchstart', touchStart);
dom.addEventListener('touchend', touchEnd);
}
//如果节点事件为空
if (!dom['event' + type]) {
//添加空数组
dom['event' + type] = [];
}
//将回调函数添加到节点事件的数组中
dom['event' + type].push(callback);
}
return {
bindEvent
}
})();