Asp.net Ajax提供的Sys.UI.DomEvent类,实现了跨浏览器的Dom事件操作,诸如以下几个常用的事件操作:$addHandler,$removeHandler,$addHandlers,$clearHandlers.
举一个简单的使用示例:
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<input id="Button1" type="button" value="button" />
<input id="txt1" type="text" />
</div>
</form>
</body>
</html>

<script type="text/javascript">

function pageLoad()
{

$addHandler(document,"click",function(e)
{
alert(e.clientX+":"+e.clientY);
});

$addHandler($get("Button1"),"click",function(e)
{
alert($get("txt1").value);
e.stopPropagation();
});
}
</script> 可以看见,通过$addHandler对一个DOM元素进行事件监听时,事件处理方法会传递进来一个默认的e参数,这一点和IE的全局的event对象的做法不同,而更符合标准的做法.
注意,这个e参数就是事件对象,而且是一个跨浏览器的事件对象,它具有如:target,clientX,clientY,keyCode,type...等常用的属性.
此外,这个DOM事件e对象,有两个比较重要的方法:preventDefault(阻止浏览器DOM元素的默认行为),stopPropagation(阻止DOM事件的冒泡传递).利用这两个方法,可以实现一些特别的需要.
其实绝大多数的Ajax库,都提供了这样的跨浏览器事件编程接口.但是,这些事件都只是"DOM事件"!我们监听一个DOM元素,在监听方法里写处理逻辑,这很简单也很正常.但是这个情况满足不了asp.net ajax组件对象系统的需要,它需要"对象级事件"的编码接口.
说到这里,必须提一下asp.net ajax的组件系统.和.net一样,asp.net ajax也引入了组件化编程的概念,它在扩展了javascript的一些功能和构建了一个完善的面向对象编程框架之外,另外构建了一个组件化编程的框架.这一点是asp.net ajax有别于其他ajax类库的一个最显著的地方,由于这一点,使得其他的类库像"游击队",而asp.net ajax更像"正规军".
所谓组件化,是说asp.net ajax提供了一个基础组件类:Sys.Component,由此基础组件类扩展而出:Sys.UI.Control,Sys.UI.Behavior,Sys.Preview.Action,AjaxControlToolkit.Animation.Animation等不同概念不同功能域的控件类,又从这些控件类,派生出整个asp.net ajax的所有组件类,实现整个系统的组件化编程...有点说远了,这里只谈事件...
组件是一个自给自足的类概念,它具有:属性,方法和事件.一个组件可以是DOM组件(如从Sys.UI.Control派生而来Sys.Preview.UI.Button),也可以是行为组件(如从Sys.UI.Behavior派生的Sys.Preview.ClickBehavior),还有可能是动画组件(如从AjaxControlToolkit.Animation.Animation派生的AjaxControlToolkit.Animation.ParentAnimation)等等.
所谓"事件",逻辑描述就是:由一个"触因",引发一个操作.
从这点讲,一般意义的"DOM事件",不过就是天然支持了一个html dom的事件系统,这个DOM事件系统,可以看作是一个"触因",我们实现"对象级事件",需要的不过是另外的一种"触因"罢了.下面我例举一个简单的示例,看看怎么在自定义的类之中实现"对象级事件":
<script type="text/javascript">

var MyClass = function(name)
{
this._name = name;
this._nameChangedHandler = new Array();//事件处理器集合
}

function MyClass$set_name(value)
{//当你执行set_name方法时,set_name方法就是一个"触因".

if(this._name != value)
{
this._name = value;
this._onNameChanged();//这个方法的调用,是实现自定义事件的根本.
}
}

function MyClass$get_name()
{
return this._name;
}
//加入name属性变化监听方法

function MyClass$add_nameChanged(handler)
{
Array.add(this._nameChangedHandler,handler);
}
//移除name属性变化监听方法

function MyClass$remove_nameChanged(handler)
{
Array.remove(this._nameChangedHandler,handler);
}
//实现name属性变化监听自事件

function MyClass$_onNameChanged()
{
var sf = this;

Array.forEach(this._nameChangedHandler,function(v,i,array)
{
v(sf,"nameChanged");//最主要的技术实现是这行代码,其实是监听方法的循环调用,"激发"了监听方法!
});
}

MyClass.prototype =
{
_name: null,
_nameChangedHandler: null,
set_name: MyClass$set_name,
get_name: MyClass$get_name,
_onNameChanged: MyClass$_onNameChanged,
add_nameChanged: MyClass$add_nameChanged,
remove_nameChanged: MyClass$remove_nameChanged
}

//监听方法1

function Fuc(sender,eventargs)
{
//我们在事件监听方法中做一些事
alert(sender.get_name()+":"+eventargs);
}
//监听方法2

function Fuc2(sender,eventargs)
{
alert(sender.get_name());
}

//创建自定义类的对象实例
var a = new MyClass("fanrong");

//加入"name属性变化事件"的监听方法
a.add_nameChanged(Fuc);
a.add_nameChanged(Fuc2);

//改变下姓名属性,会激发nameChanged事件
a.set_name("chunrong");
</script> 大家可以看到,在这个示例中,我们没有使用任何的DOM事件,但确实是演示了一个事件的监听方法的绑定和激发.
这里揭示了3点实现自定义事件的根本原理:
1)事件监听方法,是使用一个集合来存放的.这里使用的是数组.通过数组元素的增/删,实现事件监听方法的绑定和移除;
2)"激发"事件,其实就是遍历集合中的监听方法,依次加以调用,调用的时候传入事件参数;
3)属性值变化事件激发的"触因",其实现的原理是"索引器"内部的_onXXX方法的调用,如果没有set_XXX这样的属性索引器,至少"属性值变化事件"是实现不了的,因为我们需要在改变属性值的时候,有地方给我们写调用_onXXX这样"激发"事件的代码 :)
这个例子虽然小,但它是asp.net ajax中,整个组件系统实现"事件"的根本原理.一般,通过Sys.UI.Control派生的组件类,它们事件的"触因"是天然就有的DOM事件,而其他的组件类,实现事件的"触因"是各种各样的,但总有一个"点"是进行了"_onXXX"这样的事件激发方法的调用.
从这些基本原理出发,微软提供了一个Sys.EventHandlerList类,如果查看它内部的代码,可以发现它提供了一个"事件对象"集合(是一个Object对象),每一个"事件对象"通过"事件名称"对应一个数组,这个数组就是"事件监听方法集合",这一点和我上面举的例子的做法是一样的.只是Sys.EventHandlerList可以为所有的事件绑定和移除对应的事件监听方法,而我举的例子里,只是简单的说明了如何为一个事件实现事件监听方法的绑定/移除和激发.
此外,Sys.EventHandlerList并没有做"事件激发"的工作.因为"激发"事件时,需要传递具体的参数,而每一个不同的类,都有自己不同的参数需要传递,显然,这个步骤不通用,因此不能封装在Sys.EventHandlerList内中,而应该在具体编写一个类的激发事件代码时,由类开发程序员自己决定传递什么参数.