zl程序教程

您现在的位置是:首页 >  前端

当前栏目

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

jQuery方法思路 使用 分析 Clean
2023-06-13 09:14:43 时间

一、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标签

复制代码代码如下:

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

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、函数中用到的变量及函数
复制代码代码如下:

varnodeNames="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,"<selectmultiple="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表单元素的默认选中状态
 functionfixDefaultChecked(elem){
    if(rcheckableType.test(elem.type)){
        elem.defaultChecked=elem.checked;
    }
 }
 //创建一个安全的文档碎片
 functioncreateSafeFragment(document){
    varlist=nodeNames.split("|"),
    safeFrag=document.createDocumentFragment();//ie6,7,8浏览器把safeFrage作为HTMLDocument类型
    //针对ie9以下浏览器
    if(safeFrag.createElement){
        while(list.length){
            safeFrag.createElement(
                list.pop()
            );
        }
    }
    returnsafeFrag;
 }
 //模拟ES5中Array的新功能
 //该函数API:http://www.css88.com/jqapi-1.8/#p=jQuery.grep
 jQuery.extend({
    grep:function(elems,callback,inv){
        varretVal,
            ret=[],
            i=0,
            length=elems.length;
        inv=!!inv;
        //Gothroughthearray,onlysavingtheitems
        //thatpassthevalidatorfunction
        for(;i<length;i++){
            retVal=!!callback(elems[i],i);
            if(inv!==retVal){
                ret.push(elems[i]);
            }
        }
        returnret;
    }             
 });

2、源码分析
复制代码代码如下:
jQuery.extend({
    clean:function(elems,context,fragment,scripts){
        //声明变量
        vari,j,elem,tag,wrap,depth,div,hasBody,tbody,len,handleScript,jsTags,
            safe=context===document&&safeFragment,
            ret=[];
        //确保变量context为文档根节点document
        if(!context||typeofcontext.createDocumentFragment==="undefined"){
            context=document;
        }
        //Usethealready-createdsafefragmentifcontextpermits
        for(i=0;(elem=elems[i])!=null;i++){
            //如果elem为数字,则将其转换为字符串
            if(typeofelem==="number"){
                elem+="";
            }
            //如果elem为undefined,跳出本次循环
            if(!elem){
                continue;
            }
            //ConverthtmlstringintoDOMnodes
            //转换数组项(字符串)为DOM节点
            if(typeofelem==="string"){
                //如果不存在html实体编号或标签,则创建文本节点
                if(!rhtml.test(elem)){
                    elem=context.createTextNode(elem);
                }
                //处理是html标签字符串的数组项
                else{
                    //Ensureasafecontainerinwhichtorenderthehtml
                    //safe为#document-fragment类型,在ie9以下浏览器中,safe为HTMLDocument类型节点,且nodeNames数组为空
                    safe=safe||createSafeFragment(context);
                    //创建一个div元素并将其插入到文档碎片中
                    div=context.createElement("div");
                    safe.appendChild(div);
                    //Fix"XHTML"-styletagsinallbrowsers
                    //除了area,br,col,embed,hr,img,input,link,meta,param这些标签外,
                    //将开始标签末尾加入斜杠的标签转换为开始和结束标签
                    elem=elem.replace(rxhtmlTag,"<$1></$2>");
                    //Gotohtmlandback,thenpeeloffextrawrappers
                    //获取左边第一个标签元素
                    tag=(rtagName.exec(elem)||["",""])[1].toLowerCase();
                    //获取最外层元素的包裹元素,并将元素包裹在其中
                    wrap=wrapMap[tag]||wrapMap._default;
                    depth=wrap[0];
                    div.innerHTML=wrap[1]+elem+wrap[2];
                    //Movetotherightdepth
                    //如果元素的包裹深度大于1,div重新赋值为元素最近的包裹元素(即:包含第一层包裹元素)
                    while(depth--){
                        div=div.lastChild;
                    }
                    //RemoveIE"sautoinserted<tbody>fromtablefragments
                    //在IE6,7中,清除字符串中空table标签中自动加入的tbody标签(手动加入的除外)
                    if(!jQuery.support.tbody){
                        //Stringwasa<table>,*may*havespurious(伪造的)<tbody>
                        //判断字符串中是否拥有空tbody标签
                        hasBody=rtbody.test(elem);
                        //如果最外层标签为table且table中没有手动加入tbody
                        //变量tbody为div.firstChild.childNodes(自动加入的tbody标签集合)
                        tbody=tag==="table"&&!hasBody?
                            div.firstChild&&div.firstChild.childNodes:
                            //Stringwasabare<thead>or<tfoot>
                            //如果字符串中仅有一个空thead或tfoot标签
                            //变量tbody为div.childNodes(字符串中的thead和tfoot标签集合)
                            wrap[1]==="<table>"&&!hasBody?
                                div.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]);
                            }
                        }
                    }
                    //IEcompletelykillsleadingwhitespacewheninnerHTMLisused
                    //在ie9以下浏览器中,字符串以空白字符串开头,将空白字符串作为div元素的第一个文本子节点
                    if(!jQuery.support.leadingWhitespace&&rleadingWhitespace.test(elem)){
                        div.insertBefore(context.createTextNode(rleadingWhitespace.exec(elem)[0]),div.firstChild);
                    }
                    //获取已经处理完毕的div子节点集合(nodeList对象)
                    elem=div.childNodes;
                    //Takeoutoffragmentcontainer(weneedafreshdiveachtime)
                    //在下一次循环处理字符串数组项前,清除处理创建过的div元素
                    div.parentNode.removeChild(div);
                }
            }
            //如果elem为DOM节点(文本节点)
            if(elem.nodeType){
                ret.push(elem);
            }
            //将nodeList对象中节点合并到返回的数组中
            else{
                jQuery.merge(ret,elem);
            }
        }
        //Fix#11356:ClearelementsfromsafeFragment
        if(div){
            elem=div=safe=null;
        }
        //ResetdefaultCheckedforanyradiosandcheckboxes
        //abouttobeappendedtotheDOMinIE6/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);
                }elseif(typeofelem.getElementsByTagName!=="undefined"){
                    jQuery.grep(elem.getElementsByTagName("input"),fixDefaultChecked);
                }
            }
        }
        //Appendelementstoaprovideddocumentfragment
        //将ret数组中的各DOM节点插入到提供的文档碎片中
        //提取dom节点中的script节点,并添加到ret数组中,位置为其原父元素索引位置后
        if(fragment){
            //Specialhandlingofeachscriptelement
            handleScript=function(elem){
                //Checkifweconsideritexecutable
                //如果elem元素不存在type属性或者type值为javascript或者为ecmascript
                if(!elem.type||rscriptType.test(elem.type)){
                    //Detachthescriptandstoreitinthescriptsarray(ifprovided)orthefragment
                    //Returntruthytoindicatethatithasbeenhandled
                    returnscripts?
                        scripts.push(elem.parentNode?elem.parentNode.removeChild(elem):elem):
                        fragment.appendChild(elem);
                }
            };
            for(i=0;(elem=ret[i])!=null;i++){
                //Checkifwe"redoneafterhandlinganexecutablescript
                if(!(jQuery.nodeName(elem,"script")&&handleScript(elem))){
                    //Appendtofragmentandhandleembeddedscripts
                    //将elem元素添加到文档碎片中并处理嵌入的脚本(script标签元素)
                    fragment.appendChild(elem);
                    if(typeofelem.getElementsByTagName!=="undefined"){
                        //handleScriptalterstheDOM,sousejQuery.mergetoensuresnapshotiteration
                        jsTags=jQuery.grep(jQuery.merge([],elem.getElementsByTagName("script")),handleScript);
                        //Splicethescriptsintoretaftertheirformerancestorandadvanceourindexbeyondthem
                        //将script标签添加到数组,位置为其原父元素索引位置后
                        ret.splice.apply(ret,[i+1,0].concat(jsTags));
                        i+=jsTags.length;
                    }
                }
            }
        }
        returnret;
    }
 });