微软Ajax库DOM事件模型

本文探讨了Microsoft AJAX库中跨浏览器DOM事件模型的设计与实现,包括解决不同浏览器间DOM事件模型差异的方法,以及如何简化事件处理器的绑定和解除。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Microsoft Ajax Library Dom Event Modal

Tales from the Evil Empire

Bertrand Le Roy's blog 

In previous CTPs, the client-side DOM event model was the IE model. You would use attachEvent and get the event data from window.event. In other words, we had just implemented the IE model in Firefox and Safari. This didn't fly as well as we expected for a number of reasons. For instance, it wasn't very well received from a philosophical point of view: making standards-compliant browsers behave like the one non-compliant browser was interpreted by some as a malicious attempt by the Evil Empire to undermine the standardization of the Web by enforcing proprietary APIs. It wasn't. It just seemed at the time like a smart way to build cross-browser compatibility and the reason we did it this way and not the other way around is that both Safari and Firefox have extensible DOM Element prototypes whereas IE doesn't. In other words, there was no way we could make IE behave like the standard, but we could make the others behave like IE. Any other way to make a library cross-browser has to introduce a third API that abstracts the standard and proprietary APIs. This third API is of course just as proprietary as the IEism, whereas our previous approach had the advantage of not introducing a new one. Still, the implementation was fairly complex and relied on the presence of extensibility points that we had no guarantee we would find on other browsers that we may want to support in the future. Another problem with our first implementation was its reliance on the server to detect browsers and selectively send the compat scripts to the client. So we decided to change our compat layer and come back to a more conventional approach that will be easier to adapt to new browsers and that doesn't rely on the server, let alone on browser detection.

The new model for DOM events is thus introducing a new API, but at least it's closely modeled after the standard APIs so it should feel pretty familiar. There are many differences in the implementations of DOM events that we needed to abstract. The first one is in the names of the methods that you call to add an event. In standard browsers, you use add/removeEventListener, in IE it's attach/detachEvent. The event names themselves are different: "click" is "onclick" in IE. Then, you have to abstract the signature of the event handlers themselves: in IE the parameters come from the global window.event object, in other browsers they are passed as a parameter. Finally, the contents of the event parameter object are themselves widely divergent from one browser to the other: mouse buttons don't have the same values for example, and some very useful stuff like mouse positions is missing altogether from the standard.

新的DOM事件模型引入了新的API,但是至少与标准API更为接近,因此它感觉更加熟悉。在各种DOM事件实现中(例如IEFirefox),有许多差别需要抽象。首先是用来添加事件的方法名。标准使用add/removeEventListenerIE使用attach/detachEvent。各自的事件名称也有差别:"click" IE中变成了 "onclick"。于是,你不得不抽象事件处理器的签名:在IE中,参数来自全局的window.event对象,在其它浏览器中,它们作为一个参数被直接传递进来。最后,事件参数对象的内容本身也随着浏览器的不同而不同:例如,鼠标键(可能指的是左键和右键)的值也各不相同,并且一些非常有用的东西,例如鼠标位置在标准中根本没有提到。

Here's how you register a click event handler in the AJAX Library now:

$addHandler(myDomElement, "click", someFunction);

现在,在AJAX库中你可以这样注册一个单击事件处理器:

$addHandler(myDomElement, "click", someFunction);

As you can see, we use the standard event name here. $addHandler is an alias for Sys.UI.DomEvent.addHandler. You can unhook an event using $removeHandler. For instance, you should do that from your dispose methods to break circular references between your JavaScript objects and the DOM and to prevent memory leaks. From $addHandler, we wrap your function pointer into a closure that will be what will really be attached as the DOM event handler to abstract browser differences. What's nice is that you don't need to worry about that, you just provide a function that takes the event parameters object as its argument, and you write this function exactly the way you would write it for a standard-compliant browser. That means that even in IE, the event parameter object will contain standard fields: the key codes will be the right ones, as will be the mouse button values. By the way, so that you don't need to use integers when testing keys and mouse buttons, we have two enums, Sys.UI.Key and Sys.UI.MouseButton, that you can use in your event handlers:

如你所见,我们在这里使用标准的事件名字。$addHandlerSys.UI.DomEvent.addHandler的一个别名。你可以使用$removeHandler移除一个事件处理器。例如,你应该在你的dispose方法中这样做,去掉JavaScript对象和DOM之间的循环引用以避免内存泄漏。在$addHandler中,我们将你的函数指针包裹在一个closure(闭包)中。这样做的好处是你不必担心它是如何实现的,你只需提供一个以事件参数对象作为参数的函数,并且你编写这个函数的方法在与标准兼容的浏览器中都可以工作。这意味着即使在IE中,事件参数对象也将包含标准的域:key代码正是你所需要的,鼠标键值也是。顺便提一下,在测试按键和鼠标时,你不必使用整数,我们有两个枚举,Sys.UI.Key Sys.UI.MouseButton,你可以在事件处理器中使用它们:

function myClickHandler(e) {

  if (e.button === Sys.UI.MouseButton.leftButton) {

    //...

  }

}

 

function myKeyUpHandler(e) {

  if (e.keyCode === Sys.UI.Key.enter) {

    //...

  }

}

From the event handler, it's worth mentioning what the "this" pointer means. Just like in a standard event handler, it represents the DOM element the event was attached to, not necessarily the element that triggered the event. Those are different if the event bubbled up. For example, you may have subscribed to the click event of a div element and what was really clicked was a button inside of it. In this case, "this" represents the div, not the button, but you can still get to the button using the target field of the event parameter object. Now, if you're wiring events from a component, chances are you're using delegates as your handler functions. In this case, "this" still refers to your component, not to any DOM element. One more thing to note is that the native, proprietary event object can still be got from the rawEvent field of the event parameter object.

要特别注意“this”指针在事件处理器中的意思是什么。就像在一个标准的事件处理器中一样,它代表事件绑定到的DOM元素,它不一定就是触发事件的元素。如果事件bubbled up(冒泡),那么这两者之间还是有差别的。例如,你可能已经订阅了一个div元素的点击事件,但是被点击的实际是div中的一个按钮。在这种情况下,this代表div,而不是button,但是你仍然可以使用事件参数对象的target域得到这个button。现在,如果你正在wiring events from a component, chances are you're using delegates as your handler functions.在这种情况下,this仍然执行你的组件,而不是DOM元素。另一件值得注意的事情是,原生的,属性事件对象(proprietary event object),仍然可以从事件参数对象的rawEvent域(它没有在官方MS AJAX站点中列出,rawEvent指向被触发的原生DOM事件。)获得。 Another "interesting" divergence is the way you cancel an event or prevent it from bubbling up. In IE, you set returnValue to false (resp. set cancelBubble to true), whereas the standard is to call preventDefault (resp. stopPropagation). The event parameter object that you get as the argument of your handler has the two standard methods (preventDefault and stopPropagation) so you can use them without having to worry about IE.

 

另一个“有趣”的差别是取消一个事件或者阻止它冒泡的方法。在IE中,你可以将returnValue设置为false(resp. set cancelBubble to true),然而标准是调用preventDefault (resp. stopPropagation)。你得到的事件参数对象,它作为事件处理器的参数,有两个标准方法(preventDefault and stopPropagation),你可以其中任意一个而不必但是IE

The last things I'd like to show on the new DOM event model are some of the helpers we've added to make component developers' lives easier. In a control or behavior, you typically have to wire up multiple handlers. For example, an accessible hover behavior might want to subscribe to mouseover, mouseout, focus and blur. To do that, you'd typically create delegates to your handlers and then wire up these delegates to the DOM events one by one. From your "dispose" method, you'd also have to remove those handlers one by one and get rid of the delegates. Seeing that this pattern was repeated over and over again in almost any control or behavior sample, we decided to add helpers to batch those operations. So here's how you would wire up all those events:

最后一个关于DOM事件模型的示例是一些减轻组件开发者负担的工具。在一个控件或者behavior(行为)中,你一般需要绑定多个处理器。例如,一个可访问的hover行为可能需要订阅mouseover, mouseout, focus blur。为了做到这一点,你一般首先创建这些处理器的委托,然后一个一个的将这些委托绑定到DOM事件。在你的”dispose”方法中,你需要一个个的移除那些处理器以杀死委托。看到此模式几乎在每个控件或者行为中被一遍遍的重复,我们决定添加工具以批处理这些操作。因此你可以这样绑定所有的事件:

$addHandlers(this.get_element(), {

  mouseover: this._onHover,

  mouseout: this._onUnhover,

  focus: this._onHover,

  blur: this._onUnhover

}, this);

No need to create delegates here, this will be done for you under the hood. From dispose, it gets even simpler as we are keeping track of everything that was added using the $add APIs:

没有必要创建委托,底层会为你做这些事情。dispose变得更加简单,因为当使用$add API时,我们跟踪了所有添加的东西。

$clearHandlers(this.get_element());

In a future post, I'll also look at AJAX class events, which are events that you can expose from your own objects, and that are closer to .NET events than to the DOM events I've been showing here.

在以后的文章中,我也将解释AJAX类事件,你可以在你自己的对象中暴露这些事件,因此比起这里展示的DOM事件,它们更接近.NET事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值