任何框架中数据,信息交互的处理问题都占有很重要的地位,OpenLayers作为一web前端框架,通过事件流的方式完成,支持数据、信息的传递和交互,其内部设计还是相当不错的。这里整理下关于events这块的相关知识,自我巩固下
在开始分析之前我们先提出两个重要问题:
- 各个浏览器之间Dom事件的兼容性处理
- 提供自己的事件,并且模拟事件流
解决问题1:传统的,我们提供公共的方法来封装对浏览器DOM事件的调用,根据不同浏览器不同处理。 问题2 :通过把对象上监听的回调函数记录到数组中,然后发生变化的时候遍历数组,挨个传递数据信息并调用回调函数。简单的模拟也就差不多了。
OpenLayer是也基本按照这样的方案,具体内容下面介绍。
一、针对跨浏览器事件的解决方案
OpenLayers针对浏览器Dom事件的兼容性问题,提出了一个OpenLayers.Event类来辅助实现,提供了公共的方法
observe,stopObservingElement,stopObserving等来提供对事件的监听和注销操作。Event内部对象
observers记录事件信息,为每个监听事件的dom元素创建一个数组,数组中记录该元素的事件监听信息,这样方便添加、移除和流程管理。
查看代码分析:
observe 添加事件监听
- observe: function(elementParam, name, observer, useCapture) {
-
- varelement = OpenLayers.Util.getElement(elementParam);
- useCapture = useCapture ||false;
- if(name == 'keypress'&&
- (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
- || element.attachEvent)) {
- name ='keydown';
- }
-
- if(!this.observers) {
- this.observers = {};
- }
-
- if(!element._eventCacheID) {
- varidPrefix = "eventCacheID_";
- if(element.id) {
- idPrefix = element.id +"_" + idPrefix;
- }
- element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
- }
- varcacheID = element._eventCacheID;
-
- if(!this.observers[cacheID]) {
- this.observers[cacheID] = [];
- }
-
- this.observers[cacheID].push({
- 'element': element,
- 'name': name,
- 'observer': observer,
- 'useCapture': useCapture
- });
-
- if(element.addEventListener) {
- element.addEventListener(name, observer, useCapture);
- }else if (element.attachEvent) {
- element.attachEvent('on'+ name, observer);
- }
- },
stopObserving,移除监听事件
- stopObserving:function(elementParam, name, observer, useCapture) {
- useCapture = useCapture ||false;
-
- varelement = OpenLayers.Util.getElement(elementParam);
- varcacheID = element._eventCacheID;
- if(name == 'keypress') {
- if( navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||
- element.detachEvent) {
- name ='keydown';
- }
- }
-
- varfoundEntry = false;
- varelementObservers = OpenLayers.Event.observers[cacheID];
- if(elementObservers) {
-
-
- vari=0;
- while(!foundEntry && i < elementObservers.length) {
- varcacheEntry = elementObservers[i];
-
- if((cacheEntry.name == name) &&
- (cacheEntry.observer == observer) &&
- (cacheEntry.useCapture == useCapture)) {
-
- elementObservers.splice(i, 1);
- if(elementObservers.length == 0) {
- deleteOpenLayers.Event.observers[cacheID];
- }
- foundEntry =true;
- break;
- }
- i++;
- }
- }
-
-
- if(foundEntry) {
- if(element.removeEventListener) {
- element.removeEventListener(name, observer, useCapture);
- }else if (element && element.detachEvent) {
- element.detachEvent('on'+ name, observer);
- }
- }
- returnfoundEntry;
- },
stopObservingElement 这个则是遍历元素上所有的事,通过Observer{}中记录的信息,挨个调用stopObserving方法来移除监听。
OpenLayers.Event总结:
1、标记特殊按键的值信息,singletouch等
2、关键的跨浏览器的事件处理方法,比如获取事件源element();
代码如下:
- element: function(event) {
-
- return event.target || event.srcElement;
-
- }
同类的代码比较多,可以供直接调用。
3、事件的监听和取消方法。。
4、所有的Event使用同一个observers
使用:
到此为我们提供了监听事件的一套方法,如果我们有自己的dom元素,则可以通过Openlayers.Event来实现监听而不用担心跨浏览器的问题。下面代码监听div上的点击事件
- function init(){
- vardiv = document.createElement("div");
- document.body.appendChild(div);
- div.innerText ="点击测试";
-
- varobserveFun = OpenLayers.Function.bindAsEventListener(clickHandler, div);
-
- OpenLayers.Event.observe(div,"click",observeFun);
- }
- functionclickHandler(event){
-
- alert(this.innerText);
- }
这里要补充说明的就是OpenLayers.Funciton.BindAsEventListener(handler,div),这个方
法可以帮助我们在最终调用事件处理的时候,确保函数中的this对象指针指向我们传入的第二参数(即div),其内部实现比较简单,请自行查看源码。
二、关于自定义事件模拟的解决方案
OpenLayers.Events,内部事件流实现的关键环节。Events是一个可实例化的对象,有一个Event_Type数组,记录当前
events对象支持的事件类型,内部通过事件listeners[event_type],记录监听type类别的事件监听,和Event中有些类似,
包含了注册,监听的方法(un,on,regisrer,unregister)等。因为提供自定义事件的处理,所以这里多了一个事件触发的方法
triggerEvent();基本流程如此,我们依旧来看代码说明
构造函数:
- initialize: function (object, element, eventTypes, fallThrough, options) {
- OpenLayers.Util.extend(this, options);
- this.object = object;
- this.fallThrough = fallThrough;
-
- this.listeners = {};
-
- this.eventHandler = OpenLayers.Function.bindAsEventListener(
- this.handleBrowserEvent,this
- );
-
- this.clearMouseListener = OpenLayers.Function.bind(
- this.clearMouseCache,this
- );
-
- this.eventTypes = [];
- if(eventTypes != null) {
- for(vari=0, len=eventTypes.length; i<len; i++) {
- this.addEventType(eventTypes[i]);
- }
- }
-
- if(element != null) {
- this.attachToElement(element);
- }
- },
下面是关于监听浏览器事件的方法和监听后的处理代码,仔细观察会发现,每个浏览器事件都被在listeners中注册,然后处理函数中所做的无非是事件信
息的封转和转发(最后的triggerEvent)调用。设想一下,用户要是使用un,on等方式来监听浏览器事件,其实只是保存在listener中,
并没有实际添加到element对象上,但是元素本身对浏览器事件监听后的封装和触发调用,最终会导致我们遍历listener中的事件来响应。
- attachToElement:function (element) {
- if(this.element) {
- OpenLayers.Event.stopObservingElement(this.element);
- }
- this.element = element;
- for(vari=0, len=this.BROWSER_EVENTS.length; i<len; i++) {
- vareventType = this.BROWSER_EVENTS[i];
-
-
- this.addEventType(eventType);
-
-
- OpenLayers.Event.observe(element, eventType,this.eventHandler);
- }
-
- OpenLayers.Event.observe(element,"dragstart", OpenLayers.Event.stop);
- },
-
-
- handleBrowserEvent:function (evt) {
- vartype = evt.type, listeners = this.listeners[type];
- if(!listeners || listeners.length == 0) {
-
- return;
- }
-
- vartouches = evt.touches;
- if(touches && touches[0]) {
- varx = 0;
- vary = 0;
- varnum = touches.length;
- vartouch;
- for(vari=0; i<num; ++i) {
- touch = touches[i];
- x += touch.clientX;
- y += touch.clientY;
- }
- evt.clientX = x / num;
- evt.clientY = y / num;
- }
- if(this.includeXY) {
- evt.xy =this.getMousePosition(evt);
- }
- this.triggerEvent(type, evt);
- },
关于on,un,unRegister,Register等方法很简单,这里不列出来说明。注册事件的优先级我们可以通过设置事件调用在数组中的顺序来实现,这个也比较简单。
下面我们参考map来归纳下在OpenLayers中的事件监听,自定义事件流程的的顺序和正确使用方法。
1、map维持一个events实例,监听浏览器事件
2、我们使用on,un,unRegister,Register等方法,记录我们所编写的浏览器事件和自定义事件
3、我们在某些处理中调用map.event.triggerEvent来触发事件。
4、Events内部触发函数遍历listeners,查找事件监听并调用
控件部分通过handler来从map获取最初的时间信息,根据需求选择不同的事件予以信息
封装,判断和响应等等。关于handler中的事件注册等…以及在事件在控件中的信息传递都是依赖于此,可以自行查看来感受其中的美妙设计!!!所以,最
终的浏览器事件都是在map.div对象上监听的。Dom元素事件处理经过我们的handBrowserEvent变成供我们流程使用的事件,在我们的数
组中遍历被使用。
用户自定义事件的方法最好按照Map的实现方法来:
1、构建属于Dom元素的events对象(独立的事件管理类),注册需要添加的Dom事件,用户自定义事件类型信息
2、通过events上的un,on,unregister,register等方法来实现对自定义事件的操作
3、通过Dom事件,或者内部代码处理来触发自动以事件
4、实现用户自定义事件响应并处理
最后给出一个编写自己自定义事件的案例
- var div;
- function init(){
- div = document.createElement("div");
- div.innerText ="点击测试";
- document.body.appendChild(div);
-
-
- varevents = newOpenLayers.Events(div, div, ["userselect"]);
-
- div.events = events;
-
- div.events.register("userselect", div, userselectHandler)
-
-
- varobserveFun = OpenLayers.Function.bindAsEventListener(clickHandler, div);
- OpenLayers.Event.observe(div,"click",observeFun);
-
-
- }
-
-
- functionclickHandler(event){
-
- this.events.triggerEvent("userselect", event);
- }
-
- functionuserselectHandler(event){
- alert(this.innerText + event.type);
- }
|