关于前端事件的那些事儿

事件这个东西呢?是一个很神奇的东西,搞的我很烦 无论是在Android端还是前端。今天巧借这点时间呢?就准备来写一写这个东西。先写一篇前端的事件,然后再转向Android端写一篇Android端的事件 perfect...

一、 什么叫事件流

事件流的描述呢?很简单:就是描述从页面接收到事件的顺序。现今主流的两大阵营对此提出了两种相反的事件流:ie提出的称之为事件冒泡;Netscape communicator提出的则是事件捕获。那么什么是事件冒泡和事件捕获呢?

  • 事件冒泡

IE的事件流叫做事件冒泡,说白了冒泡就是从嵌套最深的那个节点接收,然后逐级向上冒泡直到document的过程。用下面的html为例子:

<!DOCTYPE html>
<html>
<head>
	<title>我是测试</title>
</head>
<body>
	<div >click Me</div>
</body>
</html>
复制代码

如果你单击了div元素,那么这个click事件就这么传递: 1、 div 2、 body 3、 html 4、document 也就是说,当我们点击了div元素之后,click事件首先在div元素上面发生,这个元素就是我们单击的元素。然后click事件就顺着DOM树向上传播,在每一级上面都会发生,一直传到document。

  • 事件捕获

Netscape communicator团队提出了另一种事件流叫做事件捕获。事件捕获的过程呢?则是从document开始一直传递到div的过程: 1、document 2、 html 3、 body 4、 div 示意图如下:

  • DOM的事件流

通常的事件流包括三个步骤:事件捕获阶段、处于目标阶段和事件冒泡阶段。用前面的html表示,大概就是这样。

事件在处于捕获阶段的时候呢?是不会接收到事件的,然后再目标阶段的时候,事件发生在div元素中,然后通过冒泡传回文档流

二、 事件的处理程序

事件呢?就是:用户或者浏览器自身执行的某种动作。比如click,load和mouseover。而相应某个事件的函数叫做事件处理程序。一般都是on开头的,onclick就是click的处理程序、onload就是load的处理程序等等... 首先 我们直接用代码来写主要的几种类型:

1、 html事件处理程序:
	<button id="myBtn1" onclick="showMessage()">我是 html事件处理程序</button>

    function showMessage(){
      alert("hello my friends!")
	}
复制代码
2、 Dom0事件处理程序:
		<button id="myBtn2">我是dom0事件处理程序</button>

        var btn2 = document.getElementById("myBtn2");
		btn.onclick = function(){
		  alert("hello my friends!");
		}
        btn.onclick = null;  //移除事件
复制代码
3、 Dom2事件处理程序:

这里和前面不同,dom2定义了两个方法:addEventListener()和removeEventListener()分别用来绑定和删除事件。所有的dom节点都包含这个方法,并且它接受三个参数:处理的事件名、作为事件处理的函数和一个布尔值。如果这个布尔值是true,表示在捕获阶段调用事件程序;如果是false,则表示在冒泡阶段调用事件处理程序。

事件绑定如下:

	<button id="myBtn">我是dom2事件处理程序</button>

    var btn = document.getElementById("myBtn");
	btn.addEventListener("click",function(){
		alert("hello klivitam!")
	},false)
复制代码

关于事件绑定和移除有这么几个要注意的点:

  • 事件多次绑定
var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
	alert("hello klivitam!")
},false)

btn.addEventListener("click",function(){
	alert("123456")
},false)
复制代码

如果重复进行添加事件的话,这样会先出按照顺序 先显示hello klivitam,然后再显示123456

  • 事件移除 先看下面一段代码
		var btn = document.getElementById("myBtn");
		btn.addEventListener("click",function(){
			alert("hello klivitam!")
		},false)

		btn.removeEventListener("click",function(){
			alert("hello klivitam!")
		},false)
复制代码

我们知道:利用addEventListener()绑定的事件,需要用removeEventListener()来将事件的监听移除,并且removeEventListener()的参数必须和addEventListener()的参数相同。这里我们虽然看似两者是相同的,但是并不能移除。这是值得注意的,针对这种情况,我们应该这么写:

		var handle = function(){
			alert("hello klivitam!")
		}
		var btn = document.getElementById("myBtn");
		btn.addEventListener("click",handle,false)

		btn.removeEventListener("click",handle,false)
复制代码
3、 IE事件处理程序:

ie中存在和dom中想同的两个方法:attachEvent和detachEvent。这两个方法接受想同的两个参数:事件处理程序的名称与事件处理程序函数。 首先我们先写一个绑定的实例:

		var btn2 = document.getElementById("myBtn2");

		btn2.attachEvent("onclick",function(){
			alert(window === this);
		})

		btn2.attachEvent("onclick",function(){
			alert("boy");
		})
复制代码

同样和dom2的一样的操作,这里只不过函数名变了。这里值得注意的是:window和this相等。当点击btn2元素的时候,首先会提示true,然后再提示boy。并且在移除代码的时候,和dom2的操作一样

		var btn2 = document.getElementById("myBtn2");

		var handle = function(){
			alert("hello klivitam!")
		}

		btn2.attachEvent("onclick",handle)

		btn2.detachEvent("onclick",handle)
复制代码
4、 跨浏览器的事件处理程序:

前面说了这么多方面的事件处理程序,那么我们在进行开发的时候很可能会需要适配很多浏览器。我们在每次事件绑定的时候,肯定要考虑到诸多版本,诸多内核的浏览器。为了解决这种逻辑,我们就可以进行封装处理。我在这里是这样进行处理的:

		var EventUtils = {
			addHandler:function(ele,type,hander){
				if(ele.addEventListener){
					ele.addEventListener(type,hander,false);
				}else if(ele.attachEvent){
					ele.attachEvent("on"+type,hander);
				}else{
					ele["on"+type] = hander;
				}
			}

			removeHandler:function(ele,type,hander){
				if(ele.removeEventListener){
					ele.removeEventListener(type,hander,false);
				}else if(ele.detachEvent){
					ele.detachEvent("on"+type,hander);
				}else{
					ele["on"+type] = null;
				}
			}
		}
复制代码

首先,我们在绑定事件的时候呢?绑定事件的时候首先判断是否支持dom2的事件处理程序,如果不支持,那么继续判断是否支持ie的事件处理程序,如若还不支持就只能用通用的事件处理程序。解绑的时候也是一样,先判断是否支持dom2事件处理程序,接着判断ie的事件处理程序,最后在都不支持的情况下使用通用的事件处理程序。

我们在使用的时候就可以这样写一下就好了:

		var btn2 = document.getElementById("myBtn2");

		var handle = function(){
			alert("hello klivitam!");
		}

		EventUtils.addHandler(btn2,"click",hander);

		EventUtils.removeHandler(btn,"click",hander);
复制代码

当然这个封装还室友点瑕疵的,比如不知道this的指向等等,当然 我在后续的文章中会继续的去讨论封装这个组件的。

三、 事件的对象

我们知道在触发dom上的某个事件对象的时候,这个对象会包含着所有与事件有关的信息。包括事件的元素、事件的类型以及其他与特定事件相关的信息。

  • dom的事件对象

兼容dom的浏览器会将一个event对象传到事件处理程序中。例如:

		btn.addEventListener("click",function(e){
			console.log(e);
		},false)

		btn.onclick = function(e){
			console.log(e);
		};
复制代码

上图是我打印出来传递的event对象中所包含的值。我在这里呢?就简单的介绍几个最基本的含义,剩下的其实大多数大家都能看懂。

属性/方法类型读写说明
bubblesboolean只读表明事件是否冒泡
cancelableboolean只读表明是否可以取消事件的默认行为
currentTargetElement只读其事件的处理程序正在处理程序的那个元素
defaultPreventedboolean只读为true表示已经调用了preventDefault(dom3新增的)
detailInteger只读与事件相关的详细细节
eventPhaseInteger只读调用事件处理程序的阶段:1表示捕获,2处于目标,3处于冒泡
preventDefault()Function只读取消事件的 默认行为,如果cancelable为true才能使用这个方法
stopImmediatePropagationFunction只读取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(dom3新增)
stopPropagationFunction只读取消事件的进一步捕获或者冒泡。如果cancelable为true的时候,才能使用这个方法
targetElement只读事件的目标
trustedBoolean只读为true表示事件是浏览器生成的。为false表示事件又开发者通过js创建的(dom3新增的)
typeString只读被触发事件的类型
viewabstractview只读与事件相关联的抽象视图,等同于发生事件的window对象

上面表格中值得注意三点的就是:

  • this是始终等于currentTarget的值,而target则只包含事件的实际目标

  • 当cancalable为true的时候,我们可以通过preventDefault函数来取消其默认行为

  • eventPhase可以用来判断事件的状态,捕获状态为1,目标对象上为2,冒泡上为3 。值得注意的是,当eventPhase为2的时候this,target,currentTarget是始终相等的。

  • ie的事件对象

和访问dom中的event不同,ie中访问event对象有几种不同的方式。来看下面的例子

btn.onclick = function(){
    var e = window.event;
	console.log(e);
};
btn.attachEvent("onclick",function(e){
    console.log(e)
})
复制代码

同样我也将ie事件的主要的几个属性也列在下面

属性/方法类型读写说明
cancelBubbleboolean读/写默认值为false,但将其设置为true就可以取消事件(和dom中的stopPropagation()方法作用相同)
returnValueboolean读写默认值为true,但将其设置为false就可以取消事件默认行为(跟dom中的preventDefault()方法的作用相同)
srcElementelement只读事件的目标(雷同dom中的target)
typeString只读被触发的事件的类型

上面也值得我们注意的几点是:

  • 在事件处理程序中(dom中),srcElemet属性等于this,但是attachEvent this指向的是window
btn.onclick = function(){
	console.log(window.event.srcElement === this); // true
};
btn.attachEvent("onclick",function(e){
    console.log(e.srcElement === this) //false
})
复制代码
  • 由前面表格所说returnValue属性相当于DOM中的preventDefault()方法,能取消事件的默认行为。在ie中,只要将returnValue设置成false,就可以组织默认行为了。

  • 跨浏览器的事件对象

前面仅仅封装了事件的绑定和解绑,但是我们考虑到我们有的时候可能会用到事件的event对象,前面的介绍可以知道ie和dom中会有些许不同,此时我们就可以对事件进行再次封装。代码如下:

	var EventUtils = {
			addHandler:function(ele,type,hander){
				if(ele.addEventListener){
					ele.addEventListener(type,hander,false);
				}else if(ele.attachEvent){
					ele.attachEvent("on"+type,hander);
				}else{
					ele["on"+type] = hander;
				}
			}

			getEvent:function(e){
				return e?e:window.event;
			}

			getTarget:function(e){
				return e.target||e.srcElement;
			}

			preventDefault:function(e){
				if(e.preventDefault){
					e.preventDefault()
				}else{
					e.returnValue = false;
				}

			}

			removeHandler:function(ele,type,hander){
				if(ele.removeEventListener){
					ele.removeEventListener(type,hander,false);
				}else if(ele.detachEvent){
					ele.detachEvent("on"+type,hander);
				}else{
					ele["on"+type] = null;
				}
			}

			stopPropagation:function(e){
				if(e.stopPropagation){
					e.stopPropagation();
				}else{
					e.cancelBubble = true;
				}
			}
		}
复制代码

这里呢?主要总结了事件冒泡的一些跨浏览器的兼容性问题,当然值得注意的是IE 不支持事件捕获的,所以这个方法不是不支持事件捕获的。

说在最后

十一呢?去过漂流,也去爬山,然后腿也瘸了。之后在家看了几天的书籍。怎么说呢?领悟到活着的好处,也领悟到活蹦乱跳的快乐了。我已经决心不在做一个秀才,突然有个瞬间觉得这个城市这么大 我却没去过几处的感觉。 嗯!关于接下来几个月的规划呢?我会每月写一篇书评,每周写一篇Android,写一篇前端、写一篇小程序的文章来鞭策自己不断的向前。敬请期待吧

转载于:https://juejin.im/post/5cdd4776518825107e289da0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值