jQuery.clean( elems, context, fragment, scripts ) 参数elems:数组,包含了待转换的HTML是代码 参数context:文档对象,该参数在方法jQuery.buildFragment()中被修正为正确的文档对象,稍后会调用它的方
jQuery.clean( elems, context, fragment, scripts )
参数elems:数组,包含了待转换的HTML是代码
参数context:文档对象,该参数在方法jQuery.buildFragment()中被修正为正确的文档对象,稍后会调用它的方法createTextNode()创建文本节点、调用方法createElement()创建临时div元素。
参数fragment:文档片段,作为存放转换后的DOM元素的占位符,该参数在jQuery.buildFragment()中被创建
参数scripts:数组,用于存放转换后的DOM元素中的script元素
var checkScriptType, script, j,
ret = [];
//修正文档对象context
context = context || document;
// 若文档对象context没有createElement方法,就尝试读取context.ownerDocument或context[0].ownwerDocument,如果都没,默认为文档对象document
if ( typeof context.createElement === "undefined" ) {
context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
}
//遍历待转换的HTML代码数组
//for循环语句完成了循环变量elem定义、赋值和判断有效性,减少了代码量
//判断elem有效性时候使用的"!=",可以同时过滤null和undefined,却不会过滤整型数字0
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
if ( typeof elem === "number" ) {
elem += ""; //如果elem是数字,让elem自加一个空字符串,把elem转换为字符串
}
//如果!elem为true,那么跳过本次循环,执行下次循环,主要用于过滤空字符串的情况,
if ( !elem ) {
continue;
}
// 若elem是字符串,即html代码,执行转换html代码为DOM元素
if ( typeof elem === "string" ) {
// rhtml = /<|&#?\w+;/
if ( !rhtml.test( elem ) ) {
//若html代码中不包含标签、字符串和数字代码,则调用原生方法document.createTextNode()创建文本节点
elem = context.createTextNode( elem );
} else {
// rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig
//修正自关闭标签
elem = elem.replace(rxhtmlTag, "<$1></$2>");
// 提取html代码中的标签部分,删除了前导空白符和左尖括号,并转换为小写赋值给变量wrap
var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
wrap = wrapMap[ tag ] || wrapMap._default,
depth = wrap[0],
div = context.createElement("div"),
safeChildNodes = safeFragment.childNodes,
remove;
// 文档对像context是当前文档对象,把临时div元素插入已创建的安全文档片段safeFragment中,
if ( context === document ) {
safeFragment.appendChild( div );
} else {
// 调用函数createSafeFragment()在文档对象context上创建一个新的安全文档片段,然后插入临时div元素
createSafeFragment( context ).appendChild( div );
}
//为html代码包裹必要的父元素,然后赋值给临时div元素的innerHTML属性,浏览器自动生成dom元素
div.innerHTML = wrap[1] + elem + wrap[2];
// 循环层层剥去必要的父标签,然后赋值给临时div元素,最终变量div将指向HTML代码对于那个的DOM元素的父元素
while ( depth-- ) {
div = div.lastChild;
}
// 移除IE6/7自动插入的空tbody元素
if ( !jQuery.support.tbody ) {
// rtbody = /<tbody/i
//检测html代码中是否含有tbody标签
var hasBody = rtbody.test(elem),
//表示HTML代码中含有table标签,没有tbody标签,浏览器生成dom元素时可能自动插入空tbody元素。
tbody = tag === "table" && !hasBody ?
div.firstChild && div.firstChild.childNodes :
// 表示为HTML代码包裹了父标签<table>,但是html代码中没有tbody标签,即html代码中含有thead、tfoot、colgroup、caption之一或多个,浏览器生成dom元素时可能自动插入空tbody元素,此时变量div指向table
wrap[1] === "<table>" && !hasBody ?
div.childNodes :
[];
//遍历tbody,移除空的tbody元素
//判断是否是tbody元素,若是删除此元素
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
tbody[ j ].parentNode.removeChild( tbody[ j ] );
}
}
}
// 插入IE6/7/8自动剔除的前导空白符
// rleadingWhitespace = /^\s+/
if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
}
elem = div.childNodes;
// Clear elements from DocumentFragment (safeFragment or otherwise)
// to avoid hoarding elements. Fixes #11356
if ( div ) {
div.parentNode.removeChild( div );
// Guard against -1 index exceptions in FF3.6
if ( safeChildNodes.length > 0 ) {
remove = safeChildNodes[ safeChildNodes.length - 1 ];
if ( remove && remove.parentNode ) {
remove.parentNode.removeChild( remove );
}
}
}
}
}
// 在ie6/7中,复选框和单选框按钮插入DOM树后,其选中状态checked会丢失,
var len;
//通过在插入前把属性checked的值赋值给属性defaultChecked,来解决这个问题
if ( !jQuery.support.appendChecked ) {
if ( elem[0] && typeof (len = elem.length) === "number" ) {
for ( j = 0; j < len; j++ ) {
//遍历转换后的DOM元素集合,在每个元素上调用函数findInputs(elem)
findInputs( elem[j] );
}
} else {
findInputs( elem );
}
}
if ( elem.nodeType ) {
ret.push( elem );
} else {
ret = jQuery.merge( ret, elem );
}
}
//如果传入了fragment,则遍历数组ret,提取所有合肥的script元素存在数组script,并把其他元素插入文档片段fragment
if ( fragment ) {
checkScriptType = function( elem ) {
return !elem.type || rscriptType.test( elem.type );
};
for ( i = 0; ret[i]; i++ ) {
script = ret[i];
if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) {
scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script );
} else {
if ( script.nodeType === 1 ) {
var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType );
ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
}
fragment.appendChild( script );
}
}
}
return ret;
}
函数findInputs()和fixDefaultChecked()
// 通过函数findInputs(elem)找出其中的复选框和单选按钮,然后调用fixDefaultChecked(elem)把属性checked的值赋值给属性defaultCheckedfunction fixDefaultChecked( elem ) {
if ( elem.type === "checkbox" || elem.type === "radio" ) {
elem.defaultChecked = elem.checked;
}
}
// Finds all inputs and passes them to fixDefaultChecked
function findInputs( elem ) {
var nodeName = ( elem.nodeName || "" ).toLowerCase();
if ( nodeName === "input" ) {
fixDefaultChecked( elem );
// Skip scripts, get other children
} else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
}
}
jQuery.clean( elems, context, fragment, scripts )执行步骤: