ExtJS框架基礎:事件模型及其經常使用功能

前言

工做中用ExtJS有一段時間了,Ext豐富的UI組件大大的提升了開發B/S應用的效率。雖然近期工做中每天都用到ExtJS,但不多對ExtJS框架原理性的東西進行過深刻學習,這兩天花了些時間學習了下。我並不推薦你們去研究ExtJS框架的源碼,雖然能夠學習其中的思想和原理,但太浪費精力了,除非你要本身寫框架。html

對於ExtJS這種框架,非遇到「雜症」的時候我以爲也不必去研究其源碼和底層的原理,對其一些機制大體有個概念,懂得怎麼用就行,這也是本篇博文的主要目的。api

Ext本身的事件機制

Ext中的事件遵循樹狀模型,和事件相關的類主要有這麼幾個:Ext.util.Observable、Ext.lib.Event、Ext.EventManager和Ext.EventObject。瀏覽器

Ext使用Ext.lib.Event、Ext.EventManager和Ext.EventObject對原生瀏覽器事件進行了封裝,最後給咱們用的是一套統一的跨瀏覽器的通用事件接口。HTML元素自己已經支持事件,爲何基本上全部的主流JS框架都要實現本身的事件機制呢?一個最主要的緣由是HTML元素對事件的處理是經過簡單的單一綁定實現的,若是不進行封裝,事件只能綁定到一個事件處理句柄。以下面代碼所示:app

var e = document.getElementById("test");
e.onclick = function() { alert("handler1") };
e.onclick = function() { alert("handler2") };

單擊test按鈕後會發現只會彈出一個顯示"handler2"的提示框,由於第一個被覆蓋。而使用像Ext、jQuery這樣的框架就不用擔憂這個問題,同一個事件能夠依次綁定多個事件處理句柄,以下代碼所示:
框架

Ext.onReady(function () {
    var test = Ext.get("test");
    test.on("click", function () {
        alert("handler1");
    });
    test.on("click", function () {
        alert("handler2");
    });
});

Ext實現本身的事件機制,緣由不少,好比爲了兼容不一樣瀏覽器之間的差別等。Ext對原生瀏覽器事件的封裝都在上面所說的幾個類中,若是在項目中要熟練應用Ext,是很是有必要了解一下和事件相關的類和經常使用函數的。下面開始介紹這些類和它們的功能。函數

Ext.util.Observable

Ext.util.Observable在Ext事件模型中有着舉足輕重的地位,位於Ext組件的頂端,爲Ext組件提供處理事件的最基本的功能。全部繼承自Ext.util.Observable類的控件均可以支持事件。能夠爲這些繼承了Ext.util.Observable的對象定義一些事件,而後爲這此事件配置監聽器。當某個事件觸發時,Ext會自動調用對應的監聽器,這些就是Ext的事件模型。
下面經過繼承Ext.util.Observable來實現一個支持事件的對象:
Ext.onReady(function () {
    //定義一個Person類。
    function Person(name) {
        this.name = name;
        this.addEvents("walk", "eat");
        this.superclass.constructor.call(this);
    }

    //一、讓Person繼承Ext.util.Observable的全部屬性,
    //   這樣Person類構造器中的addEvents和Person.superclass.constructor.call()在實例建立時纔會起做用。
    //   Person的實例就能夠應用Ext的事件相關的on、un等方法和在Person類構造器中的addEvents和Person.superclass.constructor.call()了。
    //二、添加一個info()函數,讓它返回Person信息。
    Ext.extend(Person, Ext.util.Observable, {
        info: function (event) {
            return this.name + " is " + (event ? "ing" : "doing nothing") + ".";
        }
    });

    //一、建立一個Person實例,而後爲它的事件配置好監聽器。
    //二、on是addListener的簡寫,un是removeListener簡寫
    var person = new Person("Liam");
    person.on("walk", function () {
        this.state = "walk";
        Ext.Msg.alert("event", this.name + " is walking.");
    });
    person.on("eat", function (meal) {
        this.state = "eat";
        Ext.Msg.alert("event", this.name + " is eating " + meal + ".");
    });

    //測試效果
    Ext.get("btnWalk").on("click", function () {
        person.fireEvent("walk");
    });
    Ext.get("btnEat").on("click", function () {
        person.fireEvent("eat", "breakfast");
    });
    Ext.get("btnInfo").on("click", function () {
        Ext.Msg.alert("info", person.info(person.state));
    });
});

以上代碼展現了在Ext中如何經過繼承Ext.util.Observable給一個類自定義事件,到這,咱們大概也瞭解了addListener/on、addEvents和fireEvent這些函數的基本用法,removeListener/un函數相關內容還會在本文後面介紹。若是要了解Ext.util.Observable的其餘細節,可看看Ext官方API文檔的介紹。工具

Ext.lib.Event

Ext.lib.Event是一個工具類,它封裝了不一樣瀏覽器的事件處理函數,爲上層組件提供了統一功能接口。
對於這個工具類,Ext自帶的文檔中沒有關於這個類的說明,實際中也不多直接用到這個類,只是與事件相關的那些操做最後都會歸結爲對這些底層函數的調用。
Ext.lib.Event中定義瞭如下幾個主要函數。學習

getX()、getY()、getXY(),得到發生的事件在頁面中的座標位置:測試

Ext.get("test").on("click", function () {
    alert(this.getX() + "," + this.getY());
});

getTarget(),返回事件的目標元素,該函數用來統一IE和其餘瀏覽器使用的e.target和e.srcElement:this

Ext.get("test").on("click", function (e) {
    var test = e.getTarget();
    alert(test.value);
});

on()和un(),這兩個函數就不用多說了。

preventDefault(),用於取消瀏覽器當前事件所執行的默認操做,好比阻止頁面跳轉。使用這個函數,我是否是能夠阻止彈出瀏覽器鼠標右鍵菜單呢?我用下面的代碼試了下,結果右鍵菜單並無被阻止,誰能告訴我爲何?

//鼠標右鍵事件沒有被阻止?
Ext.getDoc().on("mousedown ", function (e) {
    if (e.button == "2")
        e.preventDefault();
});

stopPropagation(),中止事件傳遞。好比divTest元素訂閱了click事件,它的子元素btnTest被click時,父元素divTest的click事件也會被觸發,stopPropagation()就是用來阻止這種事件冒泡的發生:

Ext.get("divTest").on("click", function () {
    alert("divTest clicked!");
});
Ext.get("btnTest").on("click", function (e) {
    alert("btnTest clicked!");
    //阻止事件冒泡
    e.stopPropagation();
});

 stopEvent(),中止一個事件,至關於調用preventDefault()和stopPropagation()兩個函數。

另外還有一些幾乎用不上的函數onAvailable()、getRelatedTarget()等,就再也不一一介紹了。

再次說明一下,Ext.lib.Event這個類實際中不多直接用到,用的只是上面講的一些底層通用函數,並供一些其它和事件相關的類如Ext.EventManager和Ext.EventObject的底層的調用。

Ext.EventManager

Ext.EventManager,做爲事件管理器,定義了一系列事件相關的處理函數。其中最經常使用的就是onDocumentReady和onWindowResize了。

咱們經常使用的Ext.onReady()就是Ext.EventManager.onDocumentReady()的簡寫形式,它會在頁面文檔渲染完畢但圖片等資源文件還未下載時調用啓動函數。

這裏有必要提一下衆所周知人人共憤的window.onresize事件:

function resizeProcess(width, height) {
    var p = document.createElement("p");
    p.innerText ="時間:" + new Date().toLocaleTimeString() + ", 寬:" + width + ", 高:" + height;
    document.body.appendChild(p);
}
//原生瀏覽器resize事件
window.onresize = function () {
    resizeProcess(document.documentElement.clientWidth, document.documentElement.clientWidth);
}

當爲window.onresize添加了事件處理函數resizeProcess後,會發現resizeProcess會被執行屢次,尤爲是IE六、IE七、IE8,還會出現假死,動不動就崩掉。

如圖,IE8瀏覽器會直接死掉。真心深惡通絕IE六、IE七、IE8,要是有朝一日能由於IE11的出現,IE6到IE10都被消滅,那該是多麼大快人心的事!
window.onresize事件處理函數被屢次乃至無數次觸發的問題,網上有很多解決方案,但稍微理想點的方案用起來都挺麻煩。Ext.EventManager下的onWindowResize事件處理函數就很是好的解決了這個問題:

Ext.onReady(function () {
    function resizeProcess(width, height) {
        var p = document.createElement("p");
        p.innerText = "時間:" + new Date().toLocaleTimeString() + ", 寬:" + width + ", 高:" + height;
        document.body.appendChild(p);
    }
    //Ext封裝的resize事件
    Ext.EventManager.onWindowResize(function (width, height) {
        resizeProcess(width, height);
    });
});

 

如圖,每次改變窗口大小,resizeProcess只執行了一次。

Ext.EventManager還有on/addListener、un/removeListener等函數,這些函數都是都過Ext.lib.Event實現的,這裏就再也不累述了。

Ext.EventObject

Ext.EventObject是對事件的封裝,它提供了豐富的工具函數,幫助咱們得到事件相關的信息。經過Ext.EventObject幫助文檔能夠了解到,它包含的許多函數都與Ext.lib.Event中的函數功能是相同甚至同名的,如getPageX()、getPageY()、getPageXY()和getTarget()等,這些函數實際上都是經過Ext.lib.Event實現的。

Ext.EventObject對Ext.lib.Event擴展的部分是對鼠標事件和按鍵事件的加強,定義了一系列按鍵,能夠用來判斷某個鍵是否被按下:

Ext.get("text").on("keypress", function (e) {
    if (e.getKey() == Ext.EventObject.SPACE) {
        Ext.Msg.alert("提示", "你按了空格鍵!");
    }
});

Ext.EventObject將瀏覽器事件和自定義事件結合在一塊兒使用,是對事件的封裝。若是要得到瀏覽器原始的事件,可經過Ext.EventObject的browserEvent得到。但這種原生事件在不一樣瀏覽器中可能會有很大差別,因此Ext.EventObject雖然提供該功能,但通常不建議使用。

給Ext組件添加事件處理函數

添加原生瀏覽器事件處理函數

咱們已經知道能夠經過 on/addListener的方式給HTML元素添加事件處理函數,Ext組件也能夠經過這種方式添加,以下代碼所示:
var text = new Ext.form.TextField({
    id: "text", renderTo: Ext.getBody()
});
Ext.get("text").on("mouseover", function (e) {
    alert("mouse over.");
});
//也能夠一次添加多個事件處理函數:
Ext.get("text").on({
    "mouseover": function (e) {
        alert("mouse over.");
    },
    "mouseout": function (e) {
        alert("mouse out.");
    }
});

這種方式能夠給任何原生瀏覽器所支持的事件添加處理函數。但這種方式不能用於容器類的Ext組件,如Ext.form.FieldSet、Ext.form.FormPanel和Ext.Toolbar等。

添加Ext組件事件處理函數

幾乎全部Ext組件根據自身的特性對原生事件都行了擴展,另外封裝了一套屬於本身的事件,這些事件的處理函數會能接收到與該組件相關的事件參數信息。下面代碼是給Ext組件添加事件的兩種方式:

var text1 = new Ext.form.TextField({
    id: "text1", renderTo: Ext.getBody()
});
//任何一個關於導航類鍵(arrows、tab、enter、esc等)被敲擊則觸發此事件
Ext.getCmp("tex1t").on("specialkey", function (field,e) {
    alert(field.getValue() + "," + e.getKey());
});

//也能夠在組件建立的時候添加事件處理函數:
var text2 = new Ext.form.TextField({
    id: "text2", renderTo: Ext.getBody(),
    listeners: {
        change: function (field, newValue, oldValue) {
            alert("change:" + newValue);
        },
        blur: function (field) {
            alert("blur:" + field.getValue());
        }
    }
});

但這種方式並不支持全部的原生瀏覽器事件,好比給 Ext.form.TextField 組件經過上面的方式添加 mosuseover 事件處理函數是沒有效果的。

還有一種經過 handler 屬性給 Ext 按鈕組件添加事件的方式,這種方式只針對Ext按鈕組件,以下:

var button = new Ext.Button({
    id: 'button',
    text:'按鈕',
    renderTo: Ext.getBody(),
    handler: function () {
        alert("Clicked!!!");
    }
});

移除事件處理函數

咱們已經知道可經過un/removeListener移除某個事件處理函數。值得注意的事,對於原生瀏覽器事件,用Ext.fly得到元素的方式添加的事件處理函數必須用Ext.fly得到元素的方式移除,同理,Ext.get也是同樣。但通常咱們用Ext.fly而不用Ext.get得到元素的方式添加事件處理函數,緣由Ext.fly更省內存。對於Ext組件事件,則必須經過Ext.getCmp得到組件的方式移除事件處理函數。以下代碼所示:

var text = new Ext.form.TextField({
    id: "text", renderTo: Ext.getBody(),
    listeners: {
        change: function (field, n, o) {
            alert("new value : " + n);
        }
    }
});
//事件處理函數
var handlerFn = function (e) {
    alert("mouse over.");
};

//添加mouseover事件處理函數。
Ext.get("text").on("mouseover", handlerFn);
//移除mouseover事件指定引用的處理函數。
Ext.get("text").removeListener("mouseover", handlerFn);
//移除mouseover事件全部的處理函數。
Ext.get("text").removeListener("mouseover");
//用fly得到元素的方式不能移除mouseover處理函數,由於該處理函數是經過get獲取元素添加的。
Ext.fly("text").removeListener("mouseover");
//一樣,用getCmp得到組件的方式也不能移除mouseover處理函數。
Ext.getCmp("text").removeListener("mouseover");
//移除text元素全部原生瀏覽器事件的全部處理函數。
Ext.get("text").removeAllListeners();
//得到組件的方式移除change事件全部的處理函數。
Ext.getCmp("text").removeListener("change");

對事件的一些額外的控制

事件的額外控制包括讓事件只被觸發一次、延遲事件處理和控制屢次觸發事件的間隔等。經過on/addListener函數的第4個參數的屬性來實現,讓咱們經過下面代碼來看看常見的幾個:

var button = new Ext.Button({
    id: 'button',
    text: '按鈕',
    renderTo: Ext.getBody()
});
button.on("click",
    function () {
        var el = document.createElement("p");
        el.innerHTML = new Date().toLocaleTimeString();
        document.body.appendChild(el);
    }, this, {
        single: true,//只會執行一次單擊事件。
        buffer: 1000, //間隔1秒響應,在響應前點擊無效。
        delay: 1000,//從事件觸發開始,1後纔會執行處理函數。
        stopPropagattion: true,//事件不會向上傳遞(即中止事件冒泡)。
        preventDefault: true //中止事件默認操做。
        //...
    }
);

 

結束語

ExtJS的事件模型比較複雜,提供的事件處理函數也很是之多,本文短短篇幅不可能面面具到,只是把經常使用的作了簡單介紹。本人用ExtJS也不久,難免有錯差。
但願園友們不吝指教,多多交流,隨手點個推薦,以助你們在ExtJS學習之路上快束進步。

 

參考:
《深刻淺出 Ext JS》
Ext JS API

相關文章
相關標籤/搜索