讀Ext之十三(Ext元素)


終於越來越接近Ext的核心了。這篇開始Ext.Element,這里說的Ext元素指的是Ext.Element類的實例。

 

任何前端庫都會涉及到對HTMLElement的操作,JQuery更是以其為中心,一個$()函數調用后將DOM元素以索引方式存在 JQuery對象 中。

 

Ext則使用了一個稱為 Ext.Element 的類,如 Ext.get/Ext.fly 返回的都是該類的實例對象。許多操作如樣式,添加事件等都封裝到該類里。

 

Ext.Element的定義大概框架如下

Ext.Element = function(element, forceNew){
    var dom = typeof element == "string" ?
              DOC.getElementById(element) : element,
        id;

    if(!dom) return null;
    id = dom.id;
    if(!forceNew && id && Ext.elCache[id]){ // element object already exists
        return Ext.elCache[id].el;
    }
    this.dom = dom;
    this.id = id || Ext.id(dom);
};
var D = Ext.lib.Dom,
    DH = Ext.DomHelper,
    E = Ext.lib.Event,
    A = Ext.lib.Anim,
    El = Ext.Element,
    EC = Ext.elCache;

El.prototype = {
    ...
}

可以看到,采用了的是很傳統的JS寫類方式:構造器+原型 方式。構造器內執行大概如下

 

1,如果參數element是字符串則使用document.getElementById獲取,否則直接返回element(通常是HTMLElement)。

2,給 變量 id賦值,來自dom.id

3,forceNew不傳或傳false情況下,優先從緩存中取。

4,給this掛上dom和id屬性。

 

3處有點繞人,

 

return Ext.elCache[id].el;

 

我們知道這是在定義一個類,使用new操作符。如

 

var ele = new Ext.Element('head');

 

既然是new,里面怎么又return了呢?一般是函數調用(),里面有return。構造器里一般都是定義一些字段。其實當構造器中return的是對象類型時是允許的,如果return的是基本類型,又使用new調用函數則會出現非預期的效果。對於這點感興趣的詳見:具名函數的四種調用方式 系列 。

 

構造器內其實很少代碼,比較簡單。隨后是定義了一些變量如D,DH,這些多數是之前篇幅中提到的。Ext采用這種簡單的設計模式:組合。Bruce Eckel在《Think in Java》中所言,避免濫用繼承,多數情況下組合已足以。

 

接着看掛在prototype上的第一個方法 set

set : function(o, useSet){
    var el = this.dom,
        attr,
        val,
        useSet = (useSet !== false) && !!el.setAttribute;

    for(attr in o){
        if (o.hasOwnProperty(attr)) {
            val = o[attr];
            if (attr == 'style') {
                DH.applyStyles(el, val);
            } else if (attr == 'cls') {
                el.className = val;
            } else if (useSet) {
                el.setAttribute(attr, val);
            } else {
                el[attr] = val;
            }
        }
    }
    return this;
},

該方法用來設置HTMLElement(如div)的屬性如id、style、class等。第一個參數是個對象(Hash)如{id:'uname'},第二個參數布爾類型,顧名思義useSet為true即使用setAttribute來設置屬性。

 

至於什么使用setAttribute,什么時候使用點操作符(.)給元素設置屬性。這里 給出了詳盡的解釋。

 

該方法內部使用for in來遍歷對象,我們知道默認情況下 for in 不會去遍歷原形鏈的屬性 的。

 

對象的hasOwnProperty方法對繼承於原形鏈的屬性是 檢測不到 的,即返回false。因此這里hasOwnProperty(attr) 這個判斷完全是多余的。

 

接着看,又是多個if分支

 

style,調用DH.applyStyles。但DH.applyStyles方法有bug 。

cls,設置類名(class)

useSet為true,使用setAttribute設置

最后使用中括號操作符(等價於點操作符)

 

看下一個方法 is

is : function(simpleSelector){
    return Ext.DomQuery.is(this.dom, simpleSelector);
},

該方法用來檢測dom元素是否具有給定的css選擇器。

 

如對於<div id="d1" class="container"></div>,Ext.get('d1').is('.container')返回的是true。注意字符串container前得有個點。因為是css類選擇器。

 

該方法內部調用Ext.DomQuery.is,暫不提。

 

接下來是 focus 方法,

focus : function(defer, /* private */ dom) {
    var me = this,
        dom = dom || me.dom;
    try{
        if(Number(defer)){
            me.focus.defer(defer, null, [null, dom]);
        }else{
            dom.focus();
        }
    }catch(e){}
    return me;
},

focus方法用來獲取焦點,defer為數字,即延遲函數調用的時間,單位是毫秒。

 

參數dom對於使用Ext庫的客戶端程序員來說是用不到的,僅提供給庫內部使用,即遞歸調用。

me.focus.defer(defer, null, [null, dom]);

這句較難理解,即遞歸調用。me是this,this是Ext.Element對象自身。在focus內部又調用自己。

 

me.focus.defer 中的 defer哪來的? 還記得第三篇 原型擴展 中提到的為Function.prototype擴展的defer方法嗎?沒錯,就是它。

 

如果沒有傳defer,那么直接調用dom.focus。這里使用了try catch,是因為某些情況下會出異常。

 

接下來是 blur 方法,很簡單就不說了。往下看 getValue 方法

getValue : function(asNumber){
    var val = this.dom.value;
    return asNumber ? parseInt(val, 10) : val;
},

獲取元素的value,一般是form內元素如input,select,textarea等。這里很巧妙,如果asNumber為true則偷偷的將其 轉換為數字類型 了。

 

接下來是 addListener /  removeListener / removeAllListeners / purgeAllListeners ,它們內部使用的是Ext.EventManager。第九篇 提到了,不說了。

 

往下是 addUnits 方法,該方法是內部用的。暫不提。

 

接下來是 load 方法

load : function(url, params, cb){
    Ext.Ajax.request(Ext.apply({
        params: params,
        url: url.url || url,
        callback: cb,
        el: this.dom,
        indicatorText: url.indicatorText || ''
    }, Ext.isObject(url) ? url : {}));
    return this;
},

該方法把請求返回的html片段作為該元素的innerHTML填充。內部調用的是Ext.Ajax.request。

 

接着是isBorderBox 方法

isBorderBox : function(){
    return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
},

標准模式中對於具有border的元素如select會返回1,怪異模式中對於input,select,textarea元素返回1。如果不是這些元素會直接返回Ext.isBorderBox。

isBorderBox = isIE && !isStrict,

 

接下來是 remove 方法,

remove : function(){
    var me = this,
        dom = me.dom;

    if (dom) {
        delete me.dom;
        Ext.removeNode(dom);
    }
},

該方法會刪除該dom元素,包括dom文檔中的,ext緩存中的,及dom元素上的事件handler。

 

往下是 hover 方法,

hover : function(overFn, outFn, scope, options){
    var me = this;
    me.on('mouseenter', overFn, scope || me.dom, options);
    me.on('mouseleave', outFn, scope || me.dom, options);
    return me;
},

意義及用法就不提了,內部使用me.on,on是addListener的簡寫。mouseenter和mouseleave 的實現在 第四篇 提到 了。

 

接着是 contains 方法,

contains : function(el){
    return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
},

判斷是否包含el元素,內部實現也在第四篇中提到。

 

再看 getAttribute 方法,

getAttribute : Ext.isIE ? function(name, ns){
    var d = this.dom,
        type = typeof d[ns + ":" + name];

    if(['undefined', 'unknown'].indexOf(type) == -1){
        return d[ns + ":" + name];
    }
    return d[name];
} : function(name, ns){
    var d = this.dom;
    return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
},

該方法用來獲取元素的屬性值。可以看到對於IE和非IE有兩個版本,IE中對undefined,unknow類型做了處理。其它瀏覽器則是標准的實現。

 

最后一個是 update 方法,

update : function(html) {
    if (this.dom) {
        this.dom.innerHTML = html;
    }
    return this;
}

該方法使用innerHTML來更新元素內容。

 

Element.js

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM