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

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

  1. 创建一个临时div元素,并插入一个安全文档片段中。
  2. 为HTML代码包裹必要的父标签,然后赋值给临时div元素的innerHTML属性,从而将HTML代码转换为DOM元素之后再层层剥去包裹的父元素,得到转换后的DOM元素
  3. 移除IE6/7自动插入的空tbody元素,插入IE6/7/8自动过滤的前导空白符
  4. 取到转换后的DOM元素集合
  5. 在IE6/7中修正复选框和单选按钮的选中状态
  6. 合并转换后的DOM元素
  7. 如果传入了文档片段fragment,则提取所有合法的script元素存入数组scripts,并把其他元素插入文档片段fragment
  8. 最后返回转换后的DOM元素数组
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端岚枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值