最近發現各大類庫都能利用div.innerHTML=HTML片斷來產生節點元素,再把它們插入到目標元素的各個位置上。這東西實際上就是insertAdjacentHTML,但是IE可惡的innerHTML把這優勢變成劣勢。首先innerHTML會把裡面的某些位置的空白去掉,見下面運行框的結果:
<!doctype html>
<html dir="ltr" lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>
IE的innerHTML By 司徒正美
</title>
<script type="text/javascript">
window.onload = function() {
var div = document.createElement("div");
div.innerHTML = " <td> <b>司徒</b>正美 </td> "
alert("|" + div.innerHTML + "|");
var c = div.childNodes;
alert("產生的節點個數 " + c.length);
for(var i=0,n=c.length;i<n;i++){
alert(c[i].nodeType);
if(c[i].nodeType === 1){
alert(":: "+c[i].childNodes.length);
}
}
}
</script>
</head>
<body>
<p id="p">
</p>
</body>
</html>
運行代碼
另一個可惡的地方是,在IE中以下元素的innerHTML是唯讀:col、 colgroup、frameset、html、 head、style、table、tbody、 tfoot、 thead、title 與 tr。為了收拾它們,Ext特意弄了個insertIntoTable。insertIntoTable就是利用DOM的insertBefore與appendChild來添加,情況基本同jQuery。不過jQuery是完全依賴這兩個方法,Ext還使用了insertAdjacentHTML。為了提高效率,所有類庫都不約而同地使用了文檔片段。基本流程都是通過div.innerHTML提取出節點,然後轉移到文檔片段上,然後用insertBefore與appendChild插入節點。對於Firefox,Ext還使用了createContextualFragment解析文本,直接插入其目標位置上。顯然,Ext的比jQuery是快許多的。不過jQuery的插入的不單是HTML片斷,還有各種節點與jQuery對象。下面重溫一下jQuery的工作流程吧。
show sourceview sourceprint?append: function() {
//傳入arguments對象,true為要對錶格進行特殊處理,回呼函數
return this.domManip(arguments, true, function(elem){
if (this.nodeType == 1)
this.appendChild( elem );
});
},
domManip: function( args, table, callback ) {
if ( this[0] ) {//如果存在元素節點
var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
//注意這裡是傳入三個參數
scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
first = fragment.firstChild;
if ( first )
for ( var i = 0, l = this.length; i < l; i++ )
callback.call( root(this[i], first), this.length > 1 || i > 0 ?
fragment.cloneNode(true) : fragment );
if ( scripts )
jQuery.each( scripts, evalScript );
}
return this;
function root( elem, cur ) {
return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
(elem.getElementsByTagName("tbody")[0] ||
elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
elem;
}
}
//elems為arguments對象,context為document對象,fragment為空白的文檔片段
clean: function( elems, context, fragment ) {
context = context || document;
// !context.createElement fails in IE with an error but returns typeof object
if ( typeof context.createElement === "undefined" )
//確保context為文檔對象
context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
// If a single string is passed in and its a single tag
// just do a createElement and skip the rest
//如果文檔對象裡面只有一個標籤,如<div>
//我們大概可能是在外面這樣調用它$(this).append("<div>")
//這時就直接把它裡面的元素名取出來,用document.createElement("div")建立後放進數組返回
if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
var match = /^<(w+)s*/?>$/.exec(elems[0]);
if ( match )
return [ context.createElement( match[1] ) ];
}
//利用一個div的innerHTML建立眾節點
var ret = [], scripts = [], div = context.createElement("div");
//如果我們是在外面這樣添加$(this).append("<td>表格1</td>","<td>表格1</td>","<td>表格1</td>")
//jQuery.each按它的第四種支分方式(沒有參數,有length)遍曆aguments對象,callback.call( value, i, value )
jQuery.each(elems, function(i, elem){//i為索引,elem為arguments對象裡的元素
if ( typeof elem === "number" )
elem += ;
if ( !elem )
return;
// Convert html string into DOM nodes
if ( typeof elem === "string" ) {
// Fix "XHTML"-style tags in all browsers
elem = elem.repl