浏览器内的事件流机制
什么是事件的执行机制呢?
- 思考一个问题?
- 当一个大盒子嵌套一个小盒子的时候,并且两个盒子都有点击事件
- 要不要执行
- 当元素触发一个事件的时候,其父元素也会触发相同的事件,父元素的父元素也会触发相同的事件
事件流的三个阶段:
捕获 / 目标 / 冒泡
- 事件捕获 : 从上到下、从祖先到子孙依次传递事件的过程
- 事件目标(事件源) : 触发事件的对象,你是点击在哪个元素身上了,那么这个事件的 目标 就是什么
- 事件冒泡 : 从下到上、从子孙到祖先依次传递事件的过程
- 浏览器默认启动了事件冒泡!!!
- IE和欧朋浏览器不支持事件捕获。
阻止事件传播
标准浏览器: event.stopPropagation() IE浏览器: event.cancelBubble = true; 兼容:
//阻止事件冒泡的兼容 function stopPropagation(evt){ var e = evt || window.event; e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true; } ———————————————— 版权声明:本文为CSDN博主「秃头请走开」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/cc2231293148/article/details/133342124
默认行为
- 默认行为,就是不用我们注册,它自己就存在的事情
- 比如我们点击鼠标右键的时候,会自动弹出一个菜单
- 比如我们点击 a 标签的时候,我们不需要注册点击事件,他自己就会跳转页面
- 这些不需要我们注册就能实现的事情,我们叫做 默认事件
右键菜单事件 :oncontextmenu
阻止浏览器默认行为
标准浏览器: event.preventDefault() IE浏览器: event.returnValue = false 兼容:
//阻止浏览器默认行为的兼容 function preventDefault(evt){ var e = evt || window.event; e.preventDefault ? e.preventDefault() : e.returnValue = false; // ruturn false ; 既阻止默认行为,也阻止事件冒泡 (方法二) }
return false : 既阻止默认行为,也阻止事件冒泡!
事件委托
- 就是把我要做的事情委托给别人来做
- 因为我们的冒泡机制,点击子元素的时候,也会同步触发父元素的相同事件
- 所以我们就可以把子元素的事件委托给父元素来做
将加到子元素上的事件,添加到父元素上,为了提高性能。原理是利用了事件冒泡。
- 实现事件委托
事件添加到父元素 通过事件对象获取事件源 进行过滤
- 事件源
target 目标
- target 这个属性是事件对象里面的属性,表示你点击的目标
- 当你触发点击事件的时候,你点击在哪个元素上,target 就是哪个元素
- 这个 target 也不兼容,在 IE 下要使用 srcElement
标准浏览器: event.target IE浏览器: event.srcElement
// 获取事件源的兼容 function getTarget(evt){ var e = evt || window.event; return e.target || e.srcElement; }
封装事件库
- 移动端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; }); }
- 移动端左滑右滑事件
匿名函数: 1.给函数一个变量 var 变量名 = function(){} 2.给函数前增加 运算符 + - ! 进行自调用 !function(){ } (); 3.给函数一个伪装, 让函数包含在括号里面 /* 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 } })();