您的位置:首页 > Web前端 > JQuery

jQuery源码分析研究学习笔记-jQuery.clean()(七)

2016-08-02 16:07 941 查看
jQuery.clean( elems, context, fragment, scripts )

参数elems:数组,包含了待转换的HTML是代码

参数context:文档对象,该参数在方法jQuery.buildFragment()中被修正为正确的文档对象,稍后会调用它的方法createTextNode()创建文本节点、调用方法createElement()创建临时div元素。

参数fragment:文档片段,作为存放转换后的DOM元素的占位符,该参数在jQuery.buildFragment()中被创建

参数scripts:数组,用于存放转换后的DOM元素中的script元素

clean: function( elems, context, fragment, scripts ) {
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的值赋值给属性defaultChecked

function 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 )执行步骤:

创建一个临时div元素,并插入一个安全文档片段中。

为HTML代码包裹必要的父标签,然后赋值给临时div元素的innerHTML属性,从而将HTML代码转换为DOM元素之后再层层剥去包裹的父元素,得到转换后的DOM元素

移除IE6/7自动插入的空tbody元素,插入IE6/7/8自动过滤的前导空白符

取到转换后的DOM元素集合

在IE6/7中修正复选框和单选按钮的选中状态

合并转换后的DOM元素

如果传入了文档片段fragment,则提取所有合法的script元素存入数组scripts,并把其他元素插入文档片段fragment

最后返回转换后的DOM元素数组
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息