热门:网页模板.net视频教程JQueryMVCjsonExtJs源码示例三级联动JQuery菜单
您现在的位置:.Net中文社区>> AJAX编程>>正文内容

ExtJS 2.2元素事件

发布时间:2009年01月10日点击数: 彭仁夔
返回深入学习ExtJS 2.2开发系列连载教程目录
在上一节,我们可以看出浏览器的不同,其事件模型的不完全相同,因为DOM2的标准模型并不能完全满足应用的需要,就是采用了标准模型的浏览器也会在实现中事件模型中也会附加上一些本浏览器特有的功能。

为了兼容这些浏览器的事件,所有JS框架都会扩展它的事件机制。提供统一访问方式,像代码4.3中就把不同浏览器注册事件处理函数功能兼容在addEvent函数中。对于事件机制的兼容处理主要是分成二个方面,一是对事件中Event进行兼容,二是对事件的注册、触发,去除监听这几个方面进行处理。下面我们就来分别地看看如何实现的。

4.2.1 Event对象的兼容处理:EventObject
Event对象是事件在发生时所产生的一些信息,处理函数可能通过这些信息进行相关的操作。在标准模型中,它是以参数传入到注册处理的监听函数中,而在IE模型中,它是存放在window.event的属性。不同事件所产生的信息也不尽相同。下面我们就列出Event对象在IE和Mozzilla浏览器实现的主要属性:

分类

IE

Mozzilla

键盘相关属性

altKeyaltLeft

altKey

ctrlKeyctrlLeft

metaKeyctrlKey

shiftKeyshiftLeft

shiftKey

keyCode

keyCodecharCodewhichisChar

鼠标相关属性

OffsetXOffsetY 

layerX layerY

ClientXClientY 

clientXclientY

screenXscreenY 

screenXscreenY

xY      

pageXpageY 

ButtonwheelDelta

Buttondetail

srcElement

currentTargettarget

fromElementtoElement

relatedTarget

一般属性

type

type

cancelBubblereturnValue

CancelableBubbles

               4.1  Event对象的相关属性

对于上表中的属性,它们有一些是能对应起来,有些却不能,就是能对应起来,也还是有一点差异,而Ext.EventObject就是兼容于各个浏览器,来处理这样差异,下面我们就结合这些属性和Ext.EventObject中相关方法与处理进行分析:

键盘相关属性
在上表我们可以看到对于键盘事件(keypress、keydown、keyup)它产生的基本信息在其键盘相关属性分类(当然它对于一般通常的type等也会进行设值)中。这里它主要是进行三组键的判断,它判断是否按下alt、ctrl、shift键。如果按下就会返回值或true值。对于Mozilla是返回true值。如果没有按下不返回值或false值。对于这三组键,在IE中还能进行精确地判断是按下其对应的左键还是右键,IE对于三组键都提供相对应的altLeft、ctrlLeft、shiftLeft键,如果按下是右键,那么就它们就会对应地返回值。

而在Mozilla中,它提供了metaKey,我们通常用的一般的键盘都没有键。对于如Sun等出的键盘,它的作用和ctrl键的功能差不多,正因为如此,在EventOjbects#setEvent中就有如下代码:this.ctrlKey = e.ctrlKey || e.metaKey;把meta当ctrl键看待。

其实比较难处理是还是如何去取得按键的值。在IE中它提供了keyCode来返回按键的代码,即按键对应的数字(如回车键对应13)。但是在Mozilla系列中,它提供了三个属性。keyCode对于在keydown和keyup事件中,它的作用和IE中keyCode相同,返回按键的数值。对于在keypress事件中,它返回是非字母按键的键值,如果是字母键,没有值。在keypress事件中那么如何去取键值呢,这就要通过charCode来取得其键值,而且是其小写字母的键值,也就是同时按下Shift键或大写锁定键无效。对于which,可以看作是keyCode或charCode的综合,它等于IE中的keyCode。任何事件都会取其按键值。

我们可以看出这种方式在开发使用起来是有点麻烦。EventObject中提供二个方法来简化它,使用来更为方便:
getCharCode : function(){return this.charCode || this.keyCode; },  

在getCharCode中,它的主要目标是用来取按字母按键的键值。对于IE中,它还能取到非字母按键的键值,在Moziila中,对于非keyPress事件,它也可以取到非字母按键的键值,这里在使用中要注意的是不要采用是否有返回值做为判断用户按下字母按键的判断,而是要通过getCharCode得到键值判断其是否在字母的范围之内。

它的另外一个方法:
getKey : function(){

          var k = this.keyCode || this.charCode;

          return Ext.isSafari ? (safariKeys[k] || k) : k;

        },    

除去兼容Safari浏览器,和getCharCode只有一点点区别。如果没有keyCode就用charCode中找。其实这里采用which反而更方便。在Jquery中event的fix函数中就是这样采用的:
if (!event.which&& ((event.charCode || event.charCode === 0)

                            ? event.charCode: event.keyCode))

    event.which = event.charCode || event.keyCode;

它把IE和Mozilla中所有不同的属性都用which来统一代理,也就是可以通过which来取得所有的按键的键值。不过它少于对Safari的处理。因为Safari中一些键返回值和标准Ascill中不一样,如它的回车键返回3作用其键值。

有时候,我们还要判断是否按下了特殊键,如果是那么就根据按下键值进行相关的操作。在按键很频繁或相关操作很复杂的情况下,进行这样的判断还是能提供一些效率的。特殊键包括:Ctrl,Alt,Shift,Esc,Delete、Enter等。EventObject# isSpecialKey就是进行这样的处理:
isSpecialKey : function(){

   var k = this.keyCode;

return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  

|| k == 40 || k == 27 ||(k == 16) || (k == 17) ||(k >= 18 && k <= 20)  

|| (k >= 33 && k <= 35) ||(k >= 36 && k <= 39) ||(k >= 44 && k <= 45);

},

对于这些特殊键值,能认识几个呢,为了使用在开发者在判断不直接采用这样的数值,EventObject提供了一些常量用来标明。我们可以通过EventObject来引用这些常量。

常量

其对应的值及说明

F1~F12

112~123(代表F1F12按键)

NUM_ZERO~NUM_NINE

96~105(数字键盘上代表09的数字键)

NUM_MULTIPLYNUM_PLUS

NUM_MINUSNUM_PERIOD

NUM_DIVISIONNUM_CENTER

106~111 (分别代表乘(*)、加(+)、减(-)、小数点(.)和除(\)。

12

A~Z

65~90(对应小写的a-z的字母)

ZERO~ NINE

48~57(对应09的数字)

INSERTDELETEESCSPACEBACKSPACETABENTERRETURN

454627328913

SHIFTCTRLCONTROL)、ALT

161718

LEFTUPRIGHTDOWN

37383940

PAGEUP PAGEUPENDHOME

33343536

PAUSECAPS_LOCKPRINT_SCREEN

192044

4.2 event常用键常量


现在我们就可以看看代码4.4中那些键值,先不去考虑其ctrlKey,我们看一下其判断的顺序,先是CTRL组合键àTABàENTERà DOWNàESCàSHIFTàCTRL等顺序,这是因为它们使用的频率来划分的,使用频率越高,放在前面进行判断就越少。

对于CTRL组合键,我们先来看一下KeyDown、KeyUp、keypress事件,KeyDown在按下一个键时触发,KeyUp是对按下的键松开时触发。而KeyPress则是在当用户按下和松开一个 ANSI 键时发生,即数字或字母键时才触发。

而this.type == 'keypress' && this.ctrlKey是判断当前按键的事件类型是否为keypress,即事件监听函数是否注册为keypress的事件,如果是的话,同时又CTRL键也是处在按住的状态,那么判断当前字母或数字键为特殊键,也就是CTRL组合键。

对于组合键,它还专门提供一个方法进行判断:

hasModifier : function(){

return ((this.ctrlKey || this.altKey) || this.shiftKey) ? true : false; },

这能任何事件中判断是否按下ctrl、alt、shift组合键。

鼠标相关属性
鼠标事件有click、dblclick、mousedown、mouseup、mouseover、mousemove、mouseout七种,对于这七种鼠标事件,前面4种是需要鼠标的按下的动作,mouseup是在鼠标按键松时,但是其前提是要按下键,那么怎么判断是按下什么键呢?

鼠标按键

我们先来说一下标准的鼠标,它分成左中右三个按键,左键一般是点击选中触发一个元素而用,中间键是可滚动的滑轮,它主要是用来进行页面或元素内容上下移动。右键主是触发元素的弹出菜单而用。这只是通常的的习惯用法。

对于四种需要鼠标键按下的事件,它的如何去判断按了什么键。在event对象的button属性保存按钮的标识,对于Mozilla,它把左中右三个键分别采用0、1、2来标识。在IE中它采用1、4、2来标识。它们的值是不同的,为了兼容,我们应该采用标准的0、1、2来标识。这就需要进行转换。Ext.EventObject中的下面代码就是统一鼠标按键值。
var btnMap = Ext.isIE ? {1:0,4:1,2:2} :

     (Ext.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});

在Mozilla文档中,我们可以看到对于click、dbclick这样的事件,很有可能是获到不到button值,因为经常被用户接口中途阻止。还有在IE中click、dbclick文档中都没有给出button这个属性,也能说明对于click类的事件,可能是得不到其button值。反正这个值统一起来就是0。可以直接加上。在Ext.EventObject#setEvent中有:
this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);

   if(e.type == 'click' && this.button == -1){ this.button = 0; }

那么这里为什么会有一个e.which,为什么还要减去1,这是为了和jquery的事件兼容,在jquery中它把鼠标按钮也存放在e.which中,同时它采用1、2、3来代表左中右三个按键。

把button调整了,那么在按钮这一方面,我们还要注意一个滚轮按钮,在IE中,它存在wheelDelta中,在Moziila中,它在detail之中。而且它们增量的值并不同。EventObject在getWheelDelta 中进行了统一同步的处理:
getWheelDelta : function(){

      var e = this.browserEvent;

      var delta = 0;

      if(e.wheelDelta){   delta = e.wheelDelta/120; /* IE/Opera. */      }

else if(e.detail){ delta = -e.detail/3; /* Mozilla case. */   }

      return delta;

},

Event坐标位置

当我们处理点击或移动鼠标事件时,其最需要的信息就是用户鼠标点和移动在什么地方,这个地方分成两个方面,一是坐标位置,二是点击或移动在什么元素之上。我们先来看一下其坐标位置。

根据不同的需求,坐标位置可以分成CLIENT,PAGE,SCREEN三种。Client是客户的浏览器的工作区域,工作区域起初位置为(0,0)坐标。Screen则是客户的整个屏幕,起初定位屏幕最上角。而Page则是根对于页面内容的最上角来进行定位,它和网页的内容相关。

在表4.1中,我们可以看出IE和Moziilla都有相当多的定位属性用来进行定位,下面我们来列表说明:

属性名

说明

ClientXClientY 

光标相对于浏览器工作区域的水平和垂直位置(像素)

screenXscreenY

光标相对于屏幕的水平和垂直位置(像素)

pageXpageY FF

光标相对于事件所在的文档的水平和垂直位置(像素)

OffsetXOffsetY 

光标相对于事件所在元素的最近已经定位的元素内边界位置。

xY      IE

光标相对于目标事件的父元素的外边界在x坐标上的位置。

layerXlayerYFF

光标相对于事件所在元素的最近已经定位的元素外边界位置

4.3  event的定位属性

对于ClientX和ClientY和screenX和screenY两对位属性没有什么好讲的。在Moziilla中pageX和pageY是相对页面初始位置定位,而它的layerX和layerY则是相对于鼠标点击的元素及其父辈元素中找到最近的进行了绝对定位的元素的外边界而定位。如果没有找到就相对于body来定位。而pageX是直接相对于body来定位。如果没有绝对定位时,那么它就和Page定位相同,(相差一个像素)。

在IE中,其OffsetX和OffsetY 和Mozilla中的layerX和layerY相似,不同的是offset是采用内边框做为起初位置,而layer是外边框做为位置,它们相差一个边框。IE中X和Y在文档中写着与最近定位的元素做为定位,但是在测试过程中,它和pageX和pageY是相似的,不过它不计算其Scroll内容的宽度,而Page则计算。具体测试代码见光盘。

这样的定位给人很混乱的感觉,Ext.EventObject提供了一个统一的定位方式。这个定位相对于page而定位的。它的四个方法getPageY、getPageX、getXY、getPoint分别从几个方面来反映Page的定位。getXY是数组形式定位,如[0,3]。getPoint返回是Point类型的对象,它定义一些对区域操作的方法。

我们还是先来看一下它是如何兼容计算所有浏览器的PageXY。这个具体实现的代码在ext-base.js文件中Ext.lib.Event类中的getPageX和getPageY方法。它们两个是相似的,这里我们给出定位Y的方法。
getPageY: function(ev) {

    ev = ev.browserEvent || ev;

    var y = ev.pageY;

if (!y && 0 !== y) {

      y = ev.clientY || 0;

           if (Ext.isIE) {   y += this.getScroll()[0];   }

}

它先判断是否有pageY属性,没有的话,就采用clientY的属性,对于IE,来加上文档向下或向右滚动内容的宽高。这个在getScroll中实现:

getScroll: function() {

     var dd = document.documentElement, db = document.body;

       if (dd && (dd.scrollTop || dd.scrollLeft)) {

             return [dd.scrollTop, dd.scrollLeft]; }


       else if (db) { return [db.scrollTop, db.scrollLeft]; }

else { return [0, 0];   }

},

在这里我们看到了dd和db两个变量,它们是兼容DHTML和XHTML标准的实现。所谓XHTML标准就是在页面文档顶部加上<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">。
在XHTML中,是不支持(部分支持)document.body来访问文档的document的相关属性,而是要通过document.documentElement方法,如对于页面的CSS设定document.documentElement.style.overflow='hidden';另外对于在取得网页窗口区域和获取滚动条位移距离((clientWidth、clientHeight、scrollLeft、scrollTop)时也要用document.documentElement。

现在我们就可以对事件进行client、screen、page三种方式的定位。对于如offsetXY这样的定位在实际中应用不是很多。

Event目标对象

当鼠标事件触发时,它一定会和网页中某些元素挂钩起来,首先就是触发这个事件的元素,我们称为事件源,这个元素很可能不是注册事件的元素,因为标准模型和IE模型都支持冒泡的事件传递方式(有的事件不支持),很有可能当前事件源触发的事件会冒泡到注册该事件的父节点元素来进行监听处理。

这个事件源在IE中是使用srcElement属性来表示,而在Mozilla中却是使用target。其实是同一样的元素,都采用完全相反的名称,这是它们在取名时站的角度不一样。对于注册该事件的元素,在Mozilla或标准中使用currentTarget来表示。

鼠标的mouseover、mouseout事件是一个位移的动作事件,对于mouseover,它是从另外一个元素移动到当前元素,对于mouseout事件,它从当前元素要移到另外一个元素中去。这个另外的元素在标准模式是使用RelatedTarget来表示。而在IE中,对于mouseover,它采用fromElement来表示,而mouseout采用toElement表示。

EventObject对这些属性提供了一个替代方法,如对于RelatedTarget,它提供了getRelatedTarget方法来兼容于IE。对于target和currentTarget之间的关系处理,它也提供一个getTarget方法用来更方便寻找它们之间的关系。

因为事件是采用冒泡机制来进行传递事件,,而我们通过target或srcElement得到的是事件源,很有可能会要找到其注册事件的元素,那么在IE中不能通过currentTarget来获取。EventObject#getTarget可能指定CSS selector的参数和其层次来查找其父元素。

一般属性
type属性指的是Event的类型,对于不同的浏览器,有着各种不同的类型,在DOM2标准的类型定义太少,所以每个浏览器都在其基础之上加了事件的处理。下面就列出主要且常见的type。 见附表。 除了鼠标和键盘事件,Event还有一组重要的属性就是阻止缺省动作发生和冒泡处理。在表4.1中,我们可以看出IE中Mozilla中是不同的。Mozilla中的Cancelable、Bubbles是只读属性,用来判断当前事件是否可以中断或冒泡,而IE中的cancelBubble、returnValue中用来设定其中断或冒泡的操作。Mozilla中的操作采用是preventDefault和stopPropagation。为了兼容统一处理,Ext.EventObject中stopEvent、preventDefault、stopPropagation就是提供了对原生事件中函数进行包裹来完成。其stopEvent是同时调用preventDefault和stopPropagation来一同完成,stopEvent就是默认浏览器什么都不处理了。

本站热点业务

更多模板/案例展示

关于我们 | 联系我们 | 团队日志 | 网站地图 | 网站合作