代码如下:
 
 
另一个可恶的地方是,在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插入节点。对于火狐,Ext还使用了createContextualFragment解析文本,直接插入其目标位置上。显然,Ext的比jQuery是快许多的。不过jQuery的插入的不单是HTML片断,还有各种节点与jQuery对象。下面重温一下jQuery的工作流程吧。
 代码如下:
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 it's a single tag 
 // just do a createElement and skip the rest 
 //如果文档对象里面只有一个标签,如 
 //我们大概可能是在外面这样调用它$(this).append("") 
 //这时就直接把它里面的元素名取出来,用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("
 if ( !elem ) 
 return; 
 // Convert html string into DOM nodes 
 if ( typeof elem === "string" ) { 
 // Fix "XHTML"-style tags in all browsers 
 elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ 
 return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? 
 all : 
 front + ">" + tag + ">"; 
 }); 
 // Trim whitespace, otherwise indexOf won't work as expected 
 var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); 
 var wrap =   !tags.indexOf("  tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&   !tags.indexOf("  //  matched above   !tags.indexOf("  // IE can't serialize and  tags normally   [ 0, "", "" ];   // Go to html and back, then peel off extra wrappers   // Move to the right depth   //处理IE自动插入tbody,如我们使用$('')创建HTML片断,它应该返回   // String was a   // String was a bare  or   for ( var j = tbody.length - 1; j >= 0 ; --j )   }   // IE completely kills leading whitespace when innerHTML is used   if ( elem.nodeType )   });   if ( fragment ) {   return scripts;//由于动态插入是传入三个参数,因此这里就返回了   return ret;  真是复杂的让人掉眼泪!不过jQuery的实现并不太高明,它把插入的东西统统用clean转换为节点集合,再把它们放到一个文档碎片中,然后用appendChild与insertBefore插入它们。在除了火狐外,其他浏览器都支持insertAdjactentXXX家族的今日,应该好好利用这些原生API。下面是Ext利用insertAdjactentHTML等方法实现的DomHelper方法,官网给出的数据: 这数据有点老了,而且最新3.03早就解决了在IE table插入内容的诟病(table,tbody,tr等的innerHTML都是只读,insertAdjactentHTML,pasteHTML等方法都无法修改其内容,要用又慢又标准的DOM方法才行,Ext的早期版本就在这里遭遇滑铁卢了)。可以看出,结合insertAdjactentHTML与文档碎片后,IE6插入节点的速度也得到难以置信的提升,直逼火狐。基于它,Ext开发了四个分支方法insertBefore、insertAfter、insertFirst、append,分别对应jQuery的before、after、prepend与append。不过,jQuery还把这几个方法巧妙地调换了调用者与传入参数,衍生出insertBefore、insertAfter、prependTo与appendTo这几个方法。但不管怎么说,jQuery这样一刀切的做法实现令人不敢苛同。下面是在火狐中实现insertAdjactentXXX家族的一个版本:  代码如下:  function insert(w, n) {   function insertAdjacentText(w, t) {   function insertAdjacentHTML(w, h) {   function insertAdjacentElement(w, n) {   HTMLElement.prototype.insertAdjacentText = insertAdjacentText  我们可以利用它设计出更快更合理的动态插入方法。下面是我的一些实现:  代码如下: 它们里面都是调用了两个静态方法,batch与insert。由于dom对象是类数组对象,我仿效jQuery那样为它实现了几个重要迭代器,forEach、map与filter等。一个dom对象包含复数个DOM元素,我们就可以用forEach遍历它们,执行其中的回调方法。  代码如下: insert方法执行jQuery的domManip方法相应的机能(dojo则为place方法),但insert方法每次处理一个元素节点,不像jQuery那样处理一组元素节点。群集处理已经由上面batch方法分离出去了。  代码如下: insert方法在实现火狐插入操作中,使用了W3C DOM Range对象的一些罕见方法,具体可到火狐官网查看。下面实现把字符串转换为节点,利用innerHTML这个伟大的方法。Prototype.js称之为_getContentFromAnonymousElement,但有许多问题,dojo称之为_toDom,mootools的Element.Properties.html,jQuery的clean。Ext没有这东西,它只支持传入HTML片断的insertAdjacentHTML方法,不支持传入元素节点的insertAdjacentElement。但有时,我们需要插入文本节点(并不包裹于元素节点之中),这时我们就需要用文档碎片做容器了,insert方法出场了。  代码如下:
 // option or optgroup 
 !tags.indexOf("
 [ 1, "", "
" ] ||  [ 2, " ' ", "
" ] || 
 (!tags.indexOf(" [ 3, " 
" ] || ", " 
" ] || 
 !jQuery.support.htmlSerialize &&//用于创建link元素 
 [ 1, "div", "" ] || 
 div.innerHTML = wrap[1] + elem + wrap[2];//比如"
" " + 表格1 +"
 while ( wrap[0]-- ) 
 div = div.lastChild; 
 //'',而IE会返回'
 if ( !jQuery.support.tbody ) { , *may* have spurious  
 var hasBody = / tbody = !tags.indexOf(" div.firstChild && div.firstChild.childNodes : 
 wrap[1] == "" && !hasBody ? 
 div.childNodes : 
 []; 
 //如果是自动插入的里面肯定没有内容 
 if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) 
 tbody[ j ].parentNode.removeChild( tbody[ j ] ); 
 if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) ) 
 div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild ); 
 //把所有节点做成纯数组 
 elem = jQuery.makeArray( div.childNodes ); 
 } 
 ret.push( elem ); 
 else
 //全并两个数组,merge方法会处理IE下object元素下消失了的param元素 
 ret = jQuery.merge( ret, elem ); 
 for ( var i = 0; ret[i]; i++ ) { 
 //如果第一层的childNodes就有script元素节点,就用scripts把它们收集起来,供后面用globalEval动态执行 
 if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { 
 scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); 
 } else { 
 //遍历各层节点,收集script元素节点 
 if ( ret[i].nodeType === 1 ) 
 ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) ); 
 fragment.appendChild( ret[i] ); 
 } 
 } 
 } 
}, 

(function() { 
 if ('HTMLElement' in this) { 
 if('insertAdjacentHTML' in HTMLElement.prototype) { 
 return
 } 
 } else { 
 return
 } 
 switch(w.toUpperCase()) { 
 case 'BEFOREEND' : 
 this.appendChild(n) 
 break
 case 'BEFOREBEGIN' : 
 this.parentNode.insertBefore(n, this) 
 break
 case 'AFTERBEGIN' : 
 this.insertBefore(n, this.childNodes[0]) 
 break
 case 'AFTEREND' : 
 this.parentNode.insertBefore(n, this.nextSibling) 
 break
 } 
 } 
 insert.call(this, w, document.createTextNode(t || '')) 
 } 
 var r = document.createRange() 
 r.selectNode(this) 
 insert.call(this, w, r.createContextualFragment(h)) 
 } 
 insert.call(this, w, n) 
 return n 
 } 
 HTMLElement.prototype.insertAdjacentHTML = insertAdjacentHTML 
 HTMLElement.prototype.insertAdjacentElement = insertAdjacentElement 
})() 
//四个插入方法,对应insertAdjactentHTML的四个插入位置,名字就套用jQuery的 
//stuff可以为字符串,各种节点或dom对象(一个类数组对象,便于链式操作!) 
//代码比jQuery的实现简洁漂亮吧! 
 append:function(stuff){ 
 return dom.batch(this,function(el){ 
 dom.insert(el,stuff,"beforeEnd"); 
 }); 
 }, 
 prepend:function(stuff){ 
 return dom.batch(this,function(el){ 
 dom.insert(el,stuff,"afterBegin"); 
 }); 
 }, 
 before:function(stuff){ 
 return dom.batch(this,function(el){ 
 dom.insert(el,stuff,"beforeBegin"); 
 }); 
 }, 
 after:function(stuff){ 
 return dom.batch(this,function(el){ 
 dom.insert(el,stuff,"afterEnd"); 
 }); 
 } 
batch:function(els,callback){ 
 els.forEach(callback); 
 return els;//链式操作 
},
insert : function(el,stuff,where){ 
 //定义两个全局的东西,提供内部方法调用 
 var doc = el.ownerDocument || dom.doc, 
 fragment = doc.createDocumentFragment(); 
 if(stuff.version){//如果是dom对象,则把它里面的元素节点移到文档碎片中 
 stuff.forEach(function(el){ 
 fragment.appendChild(el); 
 }) 
 stuff = fragment; 
 } 
 //供火狐与IE部分元素调用 
 dom._insertAdjacentElement = function(el,node,where){ 
 switch (where){ 
 case 'beforeBegin': 
 el.parentNode.insertBefore(node,el) 
 break; 
 case 'afterBegin': 
 el.insertBefore(node,el.firstChild); 
 break; 
 case 'beforeEnd': 
 el.appendChild(node); 
 break; 
 case 'afterEnd': 
 if (el.nextSibling) el.parentNode.insertBefore(node,el.nextSibling); 
 else el.parentNode.appendChild(node); 
 break; 
 } 
 }; 
 //供火狐调用 
 dom._insertAdjacentHTML = function(el,htmlStr,where){ 
 var range = doc.createRange(); 
 switch (where) { 
 case "beforeBegin"://before 
 range.setStartBefore(el); 
 break; 
 case "afterBegin"://after 
 range.selectNodeContents(el); 
 range.collapse(true); 
 break; 
 case "beforeEnd"://append 
 range.selectNodeContents(el); 
 range.collapse(false); 
 break; 
 case "afterEnd"://prepend 
 range.setStartAfter(el); 
 break; 
 } 
 var parsedHTML = range.createContextualFragment(htmlStr); 
 dom._insertAdjacentElement(el,parsedHTML,where); 
 }; 
 //以下元素的innerHTML在IE中是只读的,调用insertAdjacentElement进行插入就会出错 
 // col, colgroup, frameset, html, head, style, title,table, tbody, tfoot, thead, 与tr; 
 dom._insertAdjacentIEFix = function(el,htmlStr,where){ 
 var parsedHTML = dom.parseHTML(htmlStr,fragment); 
 dom._insertAdjacentElement(el,parsedHTML,where) 
 }; 
 //如果是节点则复制一份 
 stuff = stuff.nodeType ? stuff.cloneNode(true) : stuff; 
 if (el.insertAdjacentHTML) {//ie,chrome,opera,safari都已实现insertAdjactentXXX家族 
 try{//适合用于opera,safari,chrome与IE 
 el['insertAdjacent'+ (stuff.nodeType ? 'Element':'HTML')](where,stuff); 
 }catch(e){ 
 //IE的某些元素调用insertAdjacentXXX可能出错,因此使用此补丁 
 dom._insertAdjacentIEFix(el,stuff,where); 
 } 
 }else{ 
 //火狐专用 
 dom['_insertAdjacent'+ (stuff.nodeType ? 'Element':'HTML')](el,stuff,where); 
 } 
 } 
parseHTML : function(htmlStr, fragment){ 
 var div = dom.doc.createElement("div"), 
 reSingleTag = /^<(\w+)\s*\/?>$/;//匹配单个标签,如
 htmlStr += ''; 
 if(reSingleTag.test(htmlStr)){//如果str为单个标签 
 return [dom.doc.createElement(RegExp.$1)] 
 } 
 var tagWrap = { 
 option: ["select"], 
 optgroup: ["select"], 
 tbody: ["table"], 
 thead: ["table"], 
 tfoot: ["table"], 
 tr: ["table", "tbody"], 
 td: ["table", "tbody", "tr"], 
 th: ["table", "thead", "tr"], 
 legend: ["fieldset"], 
 caption: ["table"], 
 colgroup: ["table"], 
 col: ["table", "colgroup"], 
 li: ["ul"], 
 link:["div"] 
 }; 
 for(var param in tagWrap){ 
 var tw = tagWrap[param]; 
 switch (param) { 
 case "option":tw.pre = '