讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議115:實現DOM原型繼承機制 >

建議115:實現DOM原型繼承機制

符合DOM標準的瀏覽器都支持HTMLElement類,DOM文檔中所有元素都繼承於這個類,而HTMLElement對像又繼承於Element類(Element類繼承於Node類),這樣通過在HTMLElement類的原型對像上定義方法,為HTML DOM文檔中所有元素綁定函數和數據。例如:


HTMLElement.prototype.pre=function{//擴展原型方法

var e=this.previousSibling;

while(e&&e.nodeType!=1){

e=e.previousSibling;

}

return e;

}


為HTMLElement類的原型對像定義方法,這樣每個HTML DOM元素都會繼承這個方法。注意,在函數體內,應該通過關鍵字this來指向當前元素對象,而不用從參數變量中獲取當前元素。在應用這個原型方法時,可以直接把它綁定到元素後面,例如:


window.onload=function{

var e=document.getElementsByTagName(\"p\")[0];

e=e.pre;

alert(e.nodeName);

}


這樣通過HTMLElement類的原型對像prototype就可以很方便地擴展每個HTML元素的方法或屬性。但IE隱藏了這個類,禁止通過JavaScript腳本來訪問它。為了能夠兼容IE,可以通過擴展方法解決這個問題。擴展方法如下:


var DOMElement={

extend:function(name,fn){

//添加名稱為name的方法fn

if(!document.all)

//IE之外的瀏覽器都能夠訪問到HTMLElement這個類

eval(\"HTMLElement.prototype.\"+name+\"=fn\");

else{

//在IE中不能訪問HTMLElement這個類

//為了達到同樣的目的,必須重寫下面幾個函數

//document.createElement

//document.getElementById

//document.getElementsByTagName

//這幾個函數都是獲得HTML元素的方法

//修改這些方法,使通過這些方法獲得的每個元素擁有名稱為name的方法fn

var_createElement=document.createElement;

document.createElement=function(tag){

var_elem=_createElement(tag);

eval(\"_elem.\"+name+\"=fn\");//_elem[name]=fn;也可以達到同樣的目的

return_elem;

}

var_getElementById=document.getElementById;

document.getElementById=function(id){

var_elem=_getElementById(id);

eval(\"_elem.\"+name+\"=fn\");

return_elem;

}

var_getElementsByTagName=document.getElementsByTagName;

document.getElementsByTagName=function(tag){

var_arr=_getElementsByTagName(tag);

for(var_elem=0;_elem<_arr.length;_elem++)

eval(\"_arr[_elem].\"+name+\"=fn\");

return_arr;

}

}

}

};


上面擴展函數的設計思路比較靈巧,實現方法也很簡單,下面進行詳細分析。

首先,利用Document對象的All對像來判斷瀏覽器的類型,因為只有IE支持All對像集合。

對於非IE瀏覽器來說,由於它們一般都支持DOM標準模型,因此可以直接使用HTMLElement.prototype來設計原型方法,實現被所有文檔元素繼承。

對於IE來說,它不能夠對原型對像進行設計,用戶只能夠通過Document對象的getElementById或getElementsByTagName方法來獲取文檔中的元素,或者通過Document對象的createElement方法創建一個新的元素。那麼,只要把元素將要繼承的方法綁定到這些方法內部就可以間接實現所有元素都擁有這個方法。因為,獲取文檔中的元素只能夠通過這3種方法中的一種才可以實現。把住這3種方法的關口,即可實現為當前元素捎帶一個繼承方法的目的。

由於Document對像允許用戶重寫這些方法,那麼可以在重寫過程中順便加入用戶指定的方法。為了避免在重寫方法過程中破壞原方法的引用,可以先借助變量存儲原方法的引用:


var_createElement=document.createElement;


然後重寫方法,在重寫過程中先執行原方法的代碼:


var_elem=_createElement(tag);


接著,通過動態形式,為當前元素加入一個用戶的方法:


eval(\"_elem.\"+name+\"=fn\");


最後,再返回原方法執行的值,這樣既不會破壞原方法的功能,又為當前元素擴展了一個方法。具體應用的方法如下:


DOMElement.extend(\"pre\",function{

var e=this.previousSibling;

while(e&&e.nodeType!=1){

e=e.previousSibling;

}

return e;

})


在應用DOMElement.extend方法時,應確保在獲取或創建元素之前執行它,即在調用Document對象的getElementById、getElementsByTagName和createElement方法之前調用DOMElement.extend方法。在頁面初始化後為當前元素調用擴展的方法,實際上這個方法與上面示例中HTMLElement類原型方法是完全相同的。


window.onload=function{

var e=document.getElementsByTagName(\"p\")[0];

e=e.pre;

alert(e.nodeName);

}