​个人主页:前端青山
系列专栏:JavaScript篇
人终将被年少不可得之物困其一生

依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript-事件传播

目录

事件 传播

阻止事件传播

默认行为

阻止浏览器默认行为

事件委托

target

封装事件库

事件 传播

  1. 浏览器内的事件流机制

    什么是事件的执行机制呢?

    • 思考一个问题?

    • 当一个大盒子嵌套一个小盒子的时候,并且两个盒子都有点击事件

    • 你点击里面的小盒子,外面的大盒子上的点击事件要不要执行

    • 当元素触发一个事件的时候,其父元素也会触发相同的事件,父元素的父元素也会触发相同的事件

  2. 事件捕获 / 目标 / 冒泡

    1. 事件捕获 : 从上到下、从祖先到子孙依次传递事件的过程

    2. 事件目标 : 触发事件的对象,你是点击在哪个元素身上了,那么这个事件的 目标 就是什么

    3. 事件冒泡 : 从下到上、从子孙到祖先依次传递事件的过程

    4. 浏览器默认启动了事件冒泡!!!

    5. IE和欧朋浏览器不支持事件捕获

Documentbody{border: 1px solid black;}#box{width: 200px;height: 150px;background: pink;}.pox{width: 100px;height: 100px;background: yellow;}//1. htmlvar html = document.documentElement;//2. bodyvar body = document.body;//3. boxvar box = document.querySelector('#box');//4. poxvar 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 " />阻止事件传播 

Documentbody{border: 1px solid black;}#box{width: 200px;height: 150px;background: pink;}.pox{width: 100px;height: 100px;background: yellow;}//1. htmlvar html = document.documentElement;//2. bodyvar body = document.body;//3. boxvar box = document.querySelector('#box');//4. poxvar 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 " />

标准浏览器: event.stopPropagation() IE浏览器: event.cancelBubble = true; 兼容:

//阻止事件冒泡的兼容function stopPropagation(evt){var e = evt || window.event;e.stopPropagation " />默认行为 
  • 默认行为,就是不用我们注册,它自己就存在的事情

    • 比如我们点击鼠标右键的时候,会自动弹出一个菜单

    • 比如我们点击 a 标签的时候,我们不需要注册点击事件,他自己就会跳转页面

    • ...

  • 这些不需要我们注册就能实现的事情,我们叫做 默认事件

阻止浏览器默认行为

标准浏览器: event.preventDefault() IE浏览器: event.returnValue = false 兼容:

//阻止浏览器默认行为的兼容function preventDefault(evt){var e = evt || window.event;e.preventDefault ? e.preventDefault() : e.returnValue = false;}

return false : 既阻止默认行为,也阻止事件冒泡!

//获取ulvar ul = document.querySelector('ul');//右键菜单事件 oncontextmenudocument.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';}
//获取avar a = document.querySelector('a');//点击事件a.onclick = function(event){// e = event || window.event;// e.preventDefault ? e.preventDefault() : e.returnValue = false;return false; // 既阻止默认行为,也阻止事件冒泡}

事件委托

  • 就是把我要做的事情委托给别人来做

  • 因为我们的冒泡机制,点击子元素的时候,也会同步触发父元素的相同事件

  • 所以我们就可以把子元素的事件委托给父元素来做

将加到子元素上的事件,添加到父元素上,为了提高性能。原理是利用了事件冒泡。

  1. 实现事件委托

Document
  • 1
  • 2
  • 3
  • hhhhhhhhhhhh

  • 4
  • 5
  • 6
  • 7
  • pppppppppppp

  • 8
  • 9
  • 10
// //获取所有的li// var li = document.querySelectorAll('ul>li');// //遍历,添加事件// for(var i = 0,len = li.length;i < len;i ++){// li[i].onclick = function(){// alert(this.innerText);// }// }//获取ulvar 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);

事件添加到父元素 通过事件对象获取事件源 进行过滤

  1. 事件源

Document
  • 1
  • 2
  • 3
  • hhhhhhhhhhhh

  • 4
  • 5
  • 6
  • 7
  • pppppppppppp

  • 8
  • 9
  • 10
// 已知页面上有结构ul,内有10个li,每个li的内容不同,请使用事件委托的方式给每个li都绑定点击事件,点击的时候打印对应li的内容//获取ulvar 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);

target

  • target 这个属性是事件对象里面的属性,表示你点击的目标

  • 当你触发点击事件的时候,你点击在哪个元素上,target 就是哪个元素

  • 这个 target 也不兼容,在 IE 下要使用 srcElement

标准浏览器: event.target IE浏览器: event.srcElement

// 获取事件源的兼容function getTarget(evt){var e = evt || window.event;return e.target || e.srcElement;}

封装事件库

Document#box{width: 100px;height: 100px;background: red;}//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();}}})}//获取divvar div = document.querySelector('#box');bindEvent(div,function(){console.log('我摸了一下');},function(){console.log('我点击了一个div');})
  1. 移动端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) {//移动后,记录为trueisMove = 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) }//开始时间恢复为0startTime = 0;//记录移动为falseisMove = false; });}
  2. 移动端左滑右滑事件

/*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}})();