1、javascript 动态插入技术最近发现各大类库都能利用div.innerHTML=HTML片断来生成节点元素,再把它们插入到目标元素的各个位置上。这东西实际上就是insertAdjacentHTML,但是IE可恶的innerHTML把这优势变成劣势。首先innerHTML会把里面的某些位置的空白去掉,见下面运行框的结果:(复制运行) IE的innerHTML By 司徒正美 window.onload = function() var div = document.createElement(div); div.innerHTML = 司徒正美 alert(| + div.innerHTM
2、L + |); var c = div.childNodes; alert(生成的节点个数 + c.length); for(var i=0,n=c.length;in;i+) alert(ci.nodeType); if(ci.nodeType = 1) alert(: +ci.childNodes.length); 另一个可恶的地方是,在IE中以下元素的innerHTML是只读的:col、 colgroup、frameset、html、 head、style、table、tbody、 tfoot、 thead、title 与 tr。为了收拾它们,Ext特意弄了个insertIntoTabl
3、e。insertIntoTable就是利用DOM的insertBefore与appendChild来添加,情况基本同jQuery。不过jQuery是完全依赖这两个方法,Ext还使用了insertAdjacentHTML。为了提高效率,所有类库都不约而同地使用了文档碎片。基本流程都是通过div.innerHTML提取出节点,然后转移到文档碎片上,然后用insertBefore与appendChild插入节点。对于火狐,Ext还使用了createContextualFragment解析文本,直接插入其目标位置上。显然,Ext的比jQuery是快许多的。不过jQuery的插入的不单是HTML片断,还
4、有各种节点与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 ( this0 ) /如果存在元素节点 var fragment = (this0.ownerDocument | t
5、his0).createDocumentFragment(), /注意这里是传入三个参数 scripts = jQuery.clean( args, (this0.ownerDocument | this0), fragment ), first = fragment.firstChild; if ( first ) for ( var i = 0, l = this.length; i 1 | i 0 ? fragment.cloneNode(true) : fragment ); if ( scripts ) jQuery.each( scripts, evalScript ); retu
6、rn 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, fra
7、gment ) context = context | document; / !context.createElement fails in IE with an error but returns typeof object if ( typeof context.createElement = undefined ) /确保context为文档对象 context = context.ownerDocument | context0 & context0.ownerDocument | document; / If a single string is passed in and its
8、 a single tag / just do a createElement and skip the rest /如果文档对象里面只有一个标签,如 /我们大概可能是在外面这样调用它$(this).append() /这时就直接把它里面的元素名取出来,用document.createElement(div)创建后放进数组返回 if ( !fragment & elems.length = 1 & typeof elems0 = string ) var match = /$/.exec(elems0); if ( match ) return context.createElement( m
9、atch1 ) ; /利用一个div的innerHTML创建众节点 var ret = , scripts = , div = context.createElement(div); /如果我们是在外面这样添加$(this).append(表格1,表格1,表格1) /jQuery.each按它的第四种支分方式(没有参数,有length)遍历aguments对象,callback.call( value, i, value ) jQuery.each(elems, function(i, elem)/i为索引,elem为arguments对象里的元素 if ( typeof elem = num
10、ber ) elem += ; if ( !elem ) return; / Convert html string into DOM nodes if ( typeof elem = string ) / Fix XHTML-style tags in all browsers elem = elem.replace(/(*?)/g, function(all, front, tag) return tag.match(/(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? all : front + ; ); / Trim
11、whitespace, otherwise indexOf wont work as expected var tags = elem.replace(/s+/, ).substring(0, 10).toLowerCase(); var wrap = / option or optgroup !tags.indexOf(opt) & 1, , | !tags.indexOf(leg) & 1, , | tags.match(/(thead|tbody|tfoot|colg|cap)/) & 1, , | !tags.indexOf(tr) & 2, , | / matched above (
12、!tags.indexOf(td) | !tags.indexOf(th) & 3, , | !tags.indexOf(col) & 2, , | / IE cant serialize and tags normally !jQuery.support.htmlSerialize &/用于创建link元素 1, div, | 0, , ; / Go to html and back, then peel off extra wrappers div.innerHTML = wrap1 + elem + wrap2;/比如 +表格1+ / Move to the right depth wh
13、ile ( wrap0- ) div = div.lastChild; /处理IE自动插入tbody,如我们使用$()创建HTML片断,它应该返回 /,而IE会返回 if ( !jQuery.support.tbody ) / String was a , *may* have spurious var hasBody = /tbody/i.test(elem), tbody = !tags.indexOf(table) & !hasBody ? div.firstChild & div.firstChild.childNodes : / String was a bare or wrap1
14、= & !hasBody ? div.childNodes : ; for ( var j = tbody.length - 1; j = 0 ; -j ) /如果是自动插入的里面肯定没有内容 if ( jQuery.nodeName( tbody j , tbody ) & !tbody j .childNodes.length ) tbody j .parentNode.removeChild( tbody j ); / IE completely kills leading whitespace when innerHTML is used if ( !jQuery.support.le
15、adingWhitespace & /s/.test( elem ) ) div.insertBefore( context.createTextNode( elem.match(/s*/)0 ), div.firstChild ); /把所有节点做成纯数组 elem = jQuery.makeArray( div.childNodes ); if ( elem.nodeType ) ret.push( elem ); else /全并两个数组,merge方法会处理IE下object元素下消失了的param元素 ret = jQuery.merge( ret, elem ); ); if (
16、fragment ) for ( var i = 0; reti; i+ ) /如果第一层的childNodes就有script元素节点,就用scripts把它们收集起来,供后面用globalEval动态执行 if ( jQuery.nodeName( reti, script ) & (!reti.type | reti.type.toLowerCase() = text/javascript) ) scripts.push( reti.parentNode ? reti.parentNode.removeChild( reti ) : reti ); else /遍历各层节点,收集scri
17、pt元素节点 if ( reti.nodeType = 1 ) ret.splice.apply( ret, i + 1, 0.concat(jQuery.makeArray(reti.getElementsByTagName(script) ); fragment.appendChild( reti ); return scripts;/由于动态插入是传入三个参数,因此这里就返回了 return ret;,真是复杂的让人掉眼泪!不过jQuery的实现并不太高明,它把插入的东西统统用clean转换为节点集合,再把它们放到一个文档碎片中,然后用appendChild与insertBefore插入
18、它们。在除了火狐外,其他浏览器都支持insertAdjactentXXX家族的今日,应该好好利用这些原生API。下面是Ext利用insertAdjactentHTML等方法实现的DomHelper方法,官网给出的数据:Insertion MethodIE7 beta 2IE6FF 1.5Opera 9DOM.7301.35.420.280HTML Fragments.360.380.400.260Template.320.335.385.220Compiled Template.295.300.350.210数据来源:Tutorial:使用DomHelper 创建元素的DOM、HTML片断和模
19、版这数据有点老了,而且最新3.03早就解决了在IE table插入内容的诟病(table,tbody,tr等的innerHTML都是只读,insertAdjactentHTML,pasteHTML等方法都无法修改其内容,要用又慢又标准的DOM方法才行,Ext的早期版本就在这里遭遇滑铁卢了)。可以看出,结合insertAdjactentHTML与文档碎片后,IE6插入节点的速度也得到难以置信的提升,直逼火狐。基于它,Ext开发了四个分支方法insertBefore、insertAfter、insertFirst、append,分别对应jQuery的before、after、prepend与app
20、end。不过,jQuery还把这几个方法巧妙地调换了调用者与传入参数,衍生出insertBefore、insertAfter、prependTo与appendTo这几个方法。但不管怎么说,jQuery这样一刀切的做法实现令人不敢苛同。下面是在火狐中实现insertAdjactentXXX家族的一个版本:(function() if (HTMLElement in this) if(insertAdjacentHTML in HTMLElement.prototype) return else return function insert(w, n) switch(w.toUpperCase()
21、 case BEFOREEND : this.appendChild(n) break case BEFOREBEGIN : this.parentNode.insertBefore(n, this) break case AFTERBEGIN : this.insertBefore(n, this.childNodes0) break case AFTEREND : this.parentNode.insertBefore(n, this.nextSibling) break function insertAdjacentText(w, t) insert.call(this, w, doc
22、ument.createTextNode(t | ) function insertAdjacentHTML(w, h) var r = document.createRange() r.selectNode(this) insert.call(this, w, r.createContextualFragment(h) function insertAdjacentElement(w, n) insert.call(this, w, n) return n HTMLElement.prototype.insertAdjacentText = insertAdjacentText HTMLEl
23、ement.prototype.insertAdjacentHTML = insertAdjacentHTML HTMLElement.prototype.insertAdjacentElement = insertAdjacentElement)()我们可以利用它设计出更快更合理的动态插入方法。下面是我的一些实现:/四个插入方法,对应insertAdjactentHTML的四个插入位置,名字就套用jQuery的/stuff可以为字符串,各种节点或dom对象(一个类数组对象,便于链式操作!)/代码比jQuery的实现简洁漂亮吧! append:function(stuff) return do
24、m.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
25、,function(el) dom.insert(el,stuff,afterEnd); ); 它们里面都是调用了两个静态方法,batch与insert。由于dom对象是类数组对象,我仿效jQuery那样为它实现了几个重要迭代器,forEach、map与filter等。一个dom对象包含复数个DOM元素,我们就可以用forEach遍历它们,执行其中的回调方法。batch:function(els,callback) els.forEach(callback); return els;/链式操作,insert方法执行jQuery的domManip方法相应的机能(dojo则为place方法),但i
26、nsert方法每次处理一个元素节点,不像jQuery那样处理一组元素节点。群集处理已经由上面batch方法分离出去了。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.next
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1