任何框架中數(shù)據(jù),信息交互的處理問題都占有很重要的地位,OpenLayers作為一web前端框架,通過事件流的方式完成,支持?jǐn)?shù)據(jù)、信息的傳遞和交互,其內(nèi)部設(shè)計(jì)還是相當(dāng)不錯(cuò)的。這里整理下關(guān)于events這塊的相關(guān)知識(shí),自我鞏固下
在開始分析之前我們先提出兩個(gè)重要問題:
- 各個(gè)瀏覽器之間Dom事件的兼容性處理
- 提供自己的事件,并且模擬事件流
解決問題1:傳統(tǒng)的,我們提供公共的方法來封裝對(duì)瀏覽器DOM事件的調(diào)用,根據(jù)不同瀏覽器不同處理。 問題2 :通過把對(duì)象上監(jiān)聽的回調(diào)函數(shù)記錄到數(shù)組中,然后發(fā)生變化的時(shí)候遍歷數(shù)組,挨個(gè)傳遞數(shù)據(jù)信息并調(diào)用回調(diào)函數(shù)。簡(jiǎn)單的模擬也就差不多了。
OpenLayer是也基本按照這樣的方案,具體內(nèi)容下面介紹。
一、針對(duì)跨瀏覽器事件的解決方案
OpenLayers針對(duì)瀏覽器Dom事件的兼容性問題,提出了一個(gè)OpenLayers.Event類來輔助實(shí)現(xiàn),提供了公共的方法
observe,stopObservingElement,stopObserving等來提供對(duì)事件的監(jiān)聽和注銷操作。Event內(nèi)部對(duì)象
observers記錄事件信息,為每個(gè)監(jiān)聽事件的dom元素創(chuàng)建一個(gè)數(shù)組,數(shù)組中記錄該元素的事件監(jiān)聽信息,這樣方便添加、移除和流程管理。
查看代碼分析:
observe 添加事件監(jiān)聽
- 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,移除監(jiān)聽事件
- 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 這個(gè)則是遍歷元素上所有的事,通過Observer{}中記錄的信息,挨個(gè)調(diào)用stopObserving方法來移除監(jiān)聽。
OpenLayers.Event總結(jié):
1、標(biāo)記特殊按鍵的值信息,singletouch等
2、關(guān)鍵的跨瀏覽器的事件處理方法,比如獲取事件源element();
代碼如下:
- element: function(event) {
-
- return event.target || event.srcElement;
-
- }
同類的代碼比較多,可以供直接調(diào)用。
3、事件的監(jiān)聽和取消方法。。
4、所有的Event使用同一個(gè)observers
使用:
到此為我們提供了監(jiān)聽事件的一套方法,如果我們有自己的dom元素,則可以通過Openlayers.Event來實(shí)現(xiàn)監(jiān)聽而不用擔(dān)心跨瀏覽器的問題。下面代碼監(jiān)聽div上的點(diǎn)擊事件
- function init(){
- vardiv = document.createElement("div");
- document.body.appendChild(div);
- div.innerText ="點(diǎn)擊測(cè)試";
-
- varobserveFun = OpenLayers.Function.bindAsEventListener(clickHandler, div);
-
- OpenLayers.Event.observe(div,"click",observeFun);
- }
- functionclickHandler(event){
-
- alert(this.innerText);
- }
這里要補(bǔ)充說明的就是OpenLayers.Funciton.BindAsEventListener(handler,div),這個(gè)方
法可以幫助我們?cè)谧罱K調(diào)用事件處理的時(shí)候,確保函數(shù)中的this對(duì)象指針指向我們傳入的第二參數(shù)(即div),其內(nèi)部實(shí)現(xiàn)比較簡(jiǎn)單,請(qǐng)自行查看源碼。
二、關(guān)于自定義事件模擬的解決方案
OpenLayers.Events,內(nèi)部事件流實(shí)現(xiàn)的關(guān)鍵環(huán)節(jié)。Events是一個(gè)可實(shí)例化的對(duì)象,有一個(gè)Event_Type數(shù)組,記錄當(dāng)前
events對(duì)象支持的事件類型,內(nèi)部通過事件listeners[event_type],記錄監(jiān)聽type類別的事件監(jiān)聽,和Event中有些類似,
包含了注冊(cè),監(jiān)聽的方法(un,on,regisrer,unregister)等。因?yàn)樘峁┳远x事件的處理,所以這里多了一個(gè)事件觸發(fā)的方法
triggerEvent();基本流程如此,我們依舊來看代碼說明
構(gòu)造函數(shù):
- 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);
- }
- },
下面是關(guān)于監(jiān)聽瀏覽器事件的方法和監(jiān)聽后的處理代碼,仔細(xì)觀察會(huì)發(fā)現(xiàn),每個(gè)瀏覽器事件都被在listeners中注冊(cè),然后處理函數(shù)中所做的無非是事件信
息的封轉(zhuǎn)和轉(zhuǎn)發(fā)(最后的triggerEvent)調(diào)用。設(shè)想一下,用戶要是使用un,on等方式來監(jiān)聽瀏覽器事件,其實(shí)只是保存在listener中,
并沒有實(shí)際添加到element對(duì)象上,但是元素本身對(duì)瀏覽器事件監(jiān)聽后的封裝和觸發(fā)調(diào)用,最終會(huì)導(dǎo)致我們遍歷listener中的事件來響應(yīng)。
- 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);
- },
關(guān)于on,un,unRegister,Register等方法很簡(jiǎn)單,這里不列出來說明。注冊(cè)事件的優(yōu)先級(jí)我們可以通過設(shè)置事件調(diào)用在數(shù)組中的順序來實(shí)現(xiàn),這個(gè)也比較簡(jiǎn)單。
下面我們參考map來歸納下在OpenLayers中的事件監(jiān)聽,自定義事件流程的的順序和正確使用方法。
1、map維持一個(gè)events實(shí)例,監(jiān)聽瀏覽器事件
2、我們使用on,un,unRegister,Register等方法,記錄我們所編寫的瀏覽器事件和自定義事件
3、我們?cè)谀承┨幚碇姓{(diào)用map.event.triggerEvent來觸發(fā)事件。
4、Events內(nèi)部觸發(fā)函數(shù)遍歷listeners,查找事件監(jiān)聽并調(diào)用
控件部分通過handler來從map獲取最初的時(shí)間信息,根據(jù)需求選擇不同的事件予以信息
封裝,判斷和響應(yīng)等等。關(guān)于handler中的事件注冊(cè)等…以及在事件在控件中的信息傳遞都是依賴于此,可以自行查看來感受其中的美妙設(shè)計(jì)?。?!所以,最
終的瀏覽器事件都是在map.div對(duì)象上監(jiān)聽的。Dom元素事件處理經(jīng)過我們的handBrowserEvent變成供我們流程使用的事件,在我們的數(shù)
組中遍歷被使用。
用戶自定義事件的方法最好按照Map的實(shí)現(xiàn)方法來:
1、構(gòu)建屬于Dom元素的events對(duì)象(獨(dú)立的事件管理類),注冊(cè)需要添加的Dom事件,用戶自定義事件類型信息
2、通過events上的un,on,unregister,register等方法來實(shí)現(xiàn)對(duì)自定義事件的操作
3、通過Dom事件,或者內(nèi)部代碼處理來觸發(fā)自動(dòng)以事件
4、實(shí)現(xiàn)用戶自定義事件響應(yīng)并處理
最后給出一個(gè)編寫自己自定義事件的案例
- var div;
- function init(){
- div = document.createElement("div");
- div.innerText ="點(diǎn)擊測(cè)試";
- 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);
- }
|