JavaScript小特性(3) – 事件

JavaScript是一种事件驱动型的语言,它一般不会主动做什么事情,而是等待用户的行动,然后做出反应。

然而JavaScript事件处理这一功能仍然被当初的浏览器大战的后遗症所困扰着,使用的时候必须小心的进行对象检测。

浏览器事件大概可以分成3种:

鼠标事件、键盘事件、接口事件(接口事件即某种状态发生时引发的,例如页面载入、表单提交等)

 

1、事件对象、目标对象、this

要处理事件程序,我们必须要先获取到事件对象,然而他的获取方法在W3C模型和Microsoft模型中是不同的:

在W3C中,事件对象是作为第一个参数传入当前的事件处理程序中;

而对于IE浏览器,事件对象总是window.event。

不过我们只需一行代码就可以解决这个问题:

function eventHandler(e){
    e = e || window.event; //利用了或运算的特殊返回值
    //e便是我们要找的事件对象了~
}

event对象中,有我们事件处理程序所需的各种属性,但是最关键的是,这个事件是哪个对象触发的?也就是要找出我们的目标对象

囧的是,在这里,W3C和Microsoft又闹矛盾了,W3C的属性是target,IE下市srcElement,不过没事,还是一行代码解决:

var target = e.target||e.srcElement;

什么是目标对象呢,举个例子吧:

<ul id="test">
	<li><a href="#">111111</a></li>
	<li><a href="#">222222</a></li>
	<li><a href="#">333333</a></li>
</ul>
var test = document.getElementById("test");
    test.onclick = function(e){
    e = e || window.event;
    var target = e.target || e.srcElement;
    alert(target.nodeName);
    return false;
}

虽然我们的事件是在UL上注册的,但是当我们点击链接的时候,弹出的是“A”,因为A才是触发事件的目标对象(据说,在老版本的Safari里面,把目标对象当成了A里面的Text文本,解决办法就是判断target的节点类型,如果是Text,则target为Text的父节点)

在事件处理程序中,this指向的就是你将事件注册在上面的那个对象,上面的例子中就是UL。

 

顺便说一个关于目标对象的陷阱

当用户把鼠标移到一个元素之上时会触发mouseOver,移出时会触发mouseOut,移入移出的时候有可能会触发很多你意想不到的事件。

例如,你希望移过上面3个A的时候,提示一下:

test.onmouseover = function(e){
    e = e||window.event;
    var target = e.target||e.srcElement;
    alert(target.nodeName);
    //其实这里不能用alert,会打断事件的,我只是示意一下啦~
}

它的输出可能依次为:UL, LI, A, LI, LI, A, LI, LI, A, LI, UL

(如果UL没有可见部分,则不会出现)

似乎并不是我们期待的样子。原因?因为虽然你看不见那些LI、UL,但是他们所占的空间,例如padding,也会触发mouseOver事件,所以使用时要判断清楚你的目标对象

 

2、事件注册

想让浏览器在触发某事件的时候运行某个函数,你就必须要和浏览器说清楚,要做到这点,就需要注册个事件处理程序。

事件注册有3种方法。第一种很古老,就是直接在标签里面写(早就过时的方法啦~):

<a id="test" href="#" onclick="alert(this.innerHTML);">11111</a>

 

第二种,称为“传统模型”,算是最常见的一种(兼容性最好)

var test = document.getElementById("test");
test.onclick = function(){alert(this.innerHTML);}

在事件注册之后,我们还可以直接调用它:

test.onclick();

传统模型的缺点是,你无法为一个元素注册两个相同事件

test.onclick = funcion1;
test.onclick = function2; //之前注册的function1失效,被覆盖了

要解决这个问题就需要用一个函数将多个方法封装起来:

var originalFunction = test.onclick;
test.onclick = function(e){
    //获取event
    originalFunction(e);
    function1(e);
    function2(e);
}

这样写显得有点累赘,而且为了保证事件对象不丢失,还得自己传过去~ 如果不是你一个人在战斗,你可能还要判断这个事件之前是否被注册过,如果注册过,你还要把之前注册的事件封装到你的函数中去,烦!(并且此时的this失效了)

 

第三种方法是W3C和Microsoft的模型(又是两个,烦~)

W3C:

添加:addEventListener(eventName,functionName,isCapture);

移除:removeEventListener(eventName,functionName,isCapture);

3个参数:事件名称(没有on前缀的字符串),要注册的函数,是事件捕获还是事件冒泡(Boolean值)。

Microsoft:

添加:attachEvent(eventName,functionName);

移除:detachEvent(eventName,functionName);

2个参数:事件名称(on前缀的字符串),要注册的函数

一个解决W3C和Microsoft兼容的方法:

function addEventSimple(obj,evt,fn){ //remove就同理啦~
	if(obj.addEventListener){
		obj.addEventListener(evt,fn,false);
		//为了兼容,全用冒泡模型,一般也用不着捕获模型
	}else if(obj.attachEvent){
		obj.attachEvent('on'+evt,fn);
	}
}

不过,第三种方法也有个缺点,就是你无法知道你注册了那些事件,也意味着你无法移除所有注册的事件,除非你知道所有这些函数的名字。

 

3、事件冒泡与事件捕获

细心的童鞋可能早就注意到了,为啥第一小节里面,点击A会触发UL上注册的事件捏?

因为事件冒泡(有点像Java/C++里面的throw抛出异常,让上级捕获)

为了说明事件冒泡与事件捕获,直接上例子:

<ul id="test">
	<li id="testLi">
		<a id="testA" href="#">111111</a>
	</li>
</ul>
var test = document.getElementById("test");
var testLi = document.getElementById("testLi");
var testA = document.getElementById("testA");
//捕获
test.addEventListener('click',function(){alert("ul")},true);
testLi.addEventListener('click',function(){alert("li")},true);
testA.addEventListener('click',function(){alert("a")},true);
//冒泡
test.addEventListener('click',function(){alert("ul")},false);
testLi.addEventListener('click',function(){alert("li")},false);
testA.addEventListener('click',function(){alert("a")},false);

输出依次为:UL, LI, A, A, LI, UL。

事件冒泡与事件捕获示意图

弄清楚这个之后,我们就可以对注册事件进行一种委托(事件委托),即把底层元素的事件,委托到高层元素进行处理,因为很可能某个事件是作用于一系列底层元素的(例子中就是很多个A)。没有必要对每个底层元素进行绑定,注册事件过多,会加重页面的负担,并且影响性能,因为浏览器要追踪每一个你注册的事件。

 

我说:JavaScript的事件太繁琐,一篇还讲不完~

发布者

Rolf

伪文艺IT攻城师,热爱前端,热爱互联。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

[喜欢] [嘻嘻] [奋斗] [问号] [鼓掌] [泪] [酷] [强] [耶] [握手] [心] [给力] [神马] [围观] [奥特曼] more »