欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

jQuery.clean使用方法及思路分析

程序员文章站 2022-06-29 14:47:26
一、jquery.clean使用方法 jquery.clean( elems, context, fragment, scripts ); 二、思路分析 1、处理参数context,确保其为文档根节...

一、jquery.clean使用方法
jquery.clean( elems, context, fragment, scripts );
二、思路分析
1、处理参数context,确保其为文档根节点document
2、处理参数elems数组(循环遍历数组)
  2.1、elem为数字,转换为字符串
  2.2、elem为非法值,跳出本次循环
  2.3、elem为字符串
  2.4、字符串不存在实体编号或html标签,则创建文本节点
  2.5、字符串为实体编号或html标签

代码如下:


创建一个p元素并插入到文档碎片中
处理xhtml风格标签
将elem包裹起来,并将包裹后的字符串作为p的innerhtml
如果包裹深度大于1,只留下第一层包裹元素
清除在ie6,7中空table标签自动加入的tbody
将在ie9以下中剔除的开头空白字符串作为p元素的第一个文本子节点
将elem重新赋值为p的子节点集合(nodelist对象),
移除本次循环中文档碎片中的p,保持下一次循环中干净的p元素    


2.3、如果elem为文本节点,则直接添加到要返回的ret数组中,否则将elem(nodelist对象)中的节点合并到数组
  2.4、修复在ie6、7中type为radio,checkbox类型的节点的选中状态(checked)失效的bug
3、处理参数fragment
  3.1、将ret中各节点添加到文档碎片fragment中
  3.2、提取节点中的script子节点,并将其添加到ret数组中,添加的script位置为其原父元素位置后面
4、返回ret数组
三、注释分析
1、函数中用到的变量及函数

. 代码如下:


var nodenames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
wrapmap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ],
thead: [ 1, "<table>", "</table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
area: [ 1, "<map>", "</map>" ],
_default: [ 0, "", "" ]
},
rxhtmltag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
rtagname = /<([\w:]+)/,
rtbody = /<tbody/i,
rhtml = /<|&#?\w+;/,
rleadingwhitespace = /^\s+/,
rcheckabletype = /^(?:checkbox|radio)$/,
rscripttype = /\/(java|ecma)script/i;
// 设置复选框checkbox或单选框radio表单元素的默认选中状态
function fixdefaultchecked( elem ) {
if ( rcheckabletype.test( elem.type ) ) {
elem.defaultchecked = elem.checked;
}
}
// 创建一个安全的文档碎片
function createsafefragment( document ) {
var list = nodenames.split( "|" ),
safefrag = document.createdocumentfragment(); // ie6,7,8浏览器把safefrage作为htmldocument类型
// 针对ie9以下浏览器
if ( safefrag.createelement ) {
while ( list.length ) {
safefrag.createelement(
list.pop()
);
}
}
return safefrag;
}
// 模拟es5中array的新功能
// 该函数api:https://www.css88.com/jqapi-1.8/#p=jquery.grep
jquery.extend({
grep: function( elems, callback, inv ) {
var retval,
ret = [],
i = 0,
length = elems.length;
inv = !!inv;
// go through the array, only saving the items
// that pass the validator function
for ( ; i < length; i++ ) {
retval = !!callback( elems[ i ], i );
if ( inv !== retval ) {
ret.push( elems[ i ] );
}
}
return ret;
}
});


2、源码分析

. 代码如下:


jquery.extend({
clean: function( elems, context, fragment, scripts ) {
// 声明变量
var i, j, elem, tag, wrap, depth, p, hasbody, tbody, len, handlescript, jstags,
safe = context === document && safefragment,
ret = [];
// 确保变量context为文档根节点document
if ( !context || typeof context.createdocumentfragment === "undefined" ) {
context = document;
}
// use the already-created safe fragment if context permits
for ( i = 0; (elem = elems[i]) != null; i++ ) {
// 如果elem为数字,则将其转换为字符串
if ( typeof elem === "number" ) {
elem += "";
}
// 如果elem为undefined,跳出本次循环
if ( !elem ) {
continue;
}
// convert html string into dom nodes
// 转换数组项(字符串)为dom节点
if ( typeof elem === "string" ) {
// 如果不存在html实体编号或标签,则创建文本节点
if ( !rhtml.test( elem ) ) {
elem = context.createtextnode( elem );
}
// 处理是html标签字符串的数组项
else {
// ensure a safe container in which to render the html
// safe为#document-fragment类型,在ie9以下浏览器中,safe为htmldocument类型节点,且nodenames数组为空
safe = safe || createsafefragment( context );
// 创建一个p元素并将其插入到文档碎片中
p = context.createelement("p");
safe.appendchild( p );
// fix "xhtml"-style tags in all browsers
// 除了area,br,col,embed,hr,img,input,link,meta,param这些标签外,
// 将开始标签末尾加入斜杠的标签转换为开始和结束标签
elem = elem.replace(rxhtmltag, "<$1></$2>");
// go to html and back, then peel off extra wrappers
// 获取左边第一个标签元素
tag = ( rtagname.exec( elem ) || ["", ""] )[1].tolowercase();
// 获取最外层元素的包裹元素,并将元素包裹在其中
wrap = wrapmap[ tag ] || wrapmap._default;
depth = wrap[0];
p.innerhtml = wrap[1] + elem + wrap[2];
// move to the right depth
// 如果元素的包裹深度大于1,p重新赋值为元素最近的包裹元素(即:包含第一层包裹元素)
while ( depth-- ) {
p = p.lastchild;
}
// remove ie's autoinserted <tbody> from table fragments
// 在ie6,7中,清除字符串中空table标签中自动加入的tbody标签(手动加入的除外)
if ( !jquery.support.tbody ) {
// string was a <table>, *may* have spurious(伪造的) <tbody>
// 判断字符串中是否拥有空tbody标签
hasbody = rtbody.test(elem);
// 如果最外层标签为table且table中没有手动加入tbody
// 变量tbody为p.firstchild.childnodes(自动加入的tbody标签集合)
tbody = tag === "table" && !hasbody ?
p.firstchild && p.firstchild.childnodes :
// string was a bare <thead> or <tfoot>
// 如果字符串中仅有一个空thead或tfoot标签
// 变量tbody为p.childnodes(字符串中的thead和tfoot标签集合)
wrap[1] === "<table>" && !hasbody ?
p.childnodes :
[];
for ( j = tbody.length - 1; j >= 0 ; --j ) {
// 排除thead或tfoot标签
if ( jquery.nodename( tbody[ j ], "tbody" ) && !tbody[ j ].childnodes.length ) {
// 清除空table标签中自动加入的tbody
tbody[ j ].parentnode.removechild( tbody[ j ] );
}
}
}
// ie completely kills leading whitespace when innerhtml is used
// 在ie9以下浏览器中,字符串以空白字符串开头,将空白字符串作为p元素的第一个文本子节点
if ( !jquery.support.leadingwhitespace && rleadingwhitespace.test( elem ) ) {
p.insertbefore( context.createtextnode( rleadingwhitespace.exec(elem)[0] ), p.firstchild );
}
// 获取已经处理完毕的p子节点集合(nodelist对象)
elem = p.childnodes;
// take out of fragment container (we need a fresh p each time)
// 在下一次循环处理字符串数组项前,清除处理创建过的p元素
p.parentnode.removechild( p );
}
}
// 如果elem为dom节点(文本节点)
if ( elem.nodetype ) {
ret.push( elem );
}
// 将nodelist对象中节点合并到返回的数组中
else {
jquery.merge( ret, elem );
}
}
// fix #11356: clear elements from safefragment
if ( p ) {
elem = p = safe = null;
}
// reset defaultchecked for any radios and checkboxes
// about to be appended to the dom in ie 6/7 (#8060)
// 在ie6,7中,拥有checked属性的单选按钮,复选框在插入到其他标签后,选中状态会失效(下面代码修复该bug)
if ( !jquery.support.appendchecked ) {
for ( i = 0; (elem = ret[i]) != null; i++ ) {
if ( jquery.nodename( elem, "input" ) ) {
fixdefaultchecked( elem );
} else if ( typeof elem.getelementsbytagname !== "undefined" ) {
jquery.grep( elem.getelementsbytagname("input"), fixdefaultchecked );
}
}
}
// append elements to a provided document fragment
// 将ret数组中的各dom节点插入到提供的文档碎片中
// 提取dom节点中的script节点,并添加到ret数组中,位置为其原父元素索引位置后
if ( fragment ) {
// special handling of each script element
handlescript = function( elem ) {
// check if we consider it executable
// 如果elem元素不存在type属性或者type值为javascript或者为ecmascript
if ( !elem.type || rscripttype.test( elem.type ) ) {
// detach the script and store it in the scripts array (if provided) or the fragment
// return truthy to indicate that it has been handled
return scripts ?
scripts.push( elem.parentnode ? elem.parentnode.removechild( elem ) : elem ) :
fragment.appendchild( elem );
}
};
for ( i = 0; (elem = ret[i]) != null; i++ ) {
// check if we're done after handling an executable script
if ( !( jquery.nodename( elem, "script" ) && handlescript( elem ) ) ) {
// append to fragment and handle embedded scripts
// 将elem元素添加到文档碎片中并处理嵌入的脚本(script标签元素)
fragment.appendchild( elem );
if ( typeof elem.getelementsbytagname !== "undefined" ) {
// handlescript alters the dom, so use jquery.merge to ensure snapshot iteration
jstags = jquery.grep( jquery.merge( [], elem.getelementsbytagname("script") ), handlescript );
// splice the scripts into ret after their former ancestor and advance our index beyond them
// 将script标签添加到数组,位置为其原父元素索引位置后
ret.splice.apply( ret, [i + 1, 0].concat( jstags ) );
i += jstags.length;
}
}
}
}
return ret;
}
});