終於越來越接近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來更新元素內容。
