深入理解javascript动态插入技术_javascript技巧

原创
2016-05-16 17:15:591364浏览

最近发现各大类库都能利用div.innerHTML=HTML片断来生成节点元素,再把它们插入到目标元素的各个位置上。这东西实际上就是insertAdjacentHTML,但是IE可恶的innerHTML把这优势变成劣势。首先innerHTML会把里面的某些位置的空白去掉,见下面运行框的结果:

复制代码 代码如下:






<BR> IE的innerHTML By 司徒正美<BR>






另一个可恶的地方是,在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("表格1","表格1","表格1")
//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.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
all :
front + ">";
});

// Trim whitespace, otherwise indexOf won't work as expected
var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();

var wrap =
// option or optgroup
!tags.indexOf(" [ 1, "" ] ||

!tags.indexOf(" [ 1, "
", "
" ] ||

tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
[ 1, "", "
" ] ||

!tags.indexOf(" [ 2, "", "
" ] ||

// matched above
(!tags.indexOf(" [ 3, "", "
" ] ||

!tags.indexOf(" [ 2, "", "
" ] ||

// IE can't serialize and