zoukankan      html  css  js  c++  java
  • jquery 源码分析十二

    在Sizzle中只剩下最后一段大的函数了,就是select,也就是在sizzle中联系compile和Sizzle函数的关键。它在Sizzle中主要处理那些并非是简单的选择器,如包含有:eq(1)的。

    直接看源码:

    function select( selector, context, results, seed ) {
        var i, tokens, token, type, find,
            match = tokenize( selector );
    
        if ( !seed ) {
            // 如果只有一个group的话,就减少分析,直接用以下的方法
            if ( match.length === 1 ) {
    
                // 如果第一个选择符是ID的话,就直接取出来当做上下文
                tokens = match[0] = match[0].slice( 0 );
                if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
                        support.getById && context.nodeType === 9 && documentIsHTML &&
                        Expr.relative[ tokens[1].type ] ) {
    
                    // 返回context下的特定ID的元素
                    context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
                    if ( !context ) {
                        return results;
                    }
                    selector = selector.slice( tokens.shift().value.length );
                }
    
                // 如果有:eq(1)或者>出现在开头就从左往右,否则就是从右到左
                i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
                while ( i-- ) {
                    token = tokens[i];
    
                    // 如果有' ','>','+','~',出现就直接break
                    if ( Expr.relative[ (type = token.type) ] ) {
                        break;
                    }
                    if ( (find = Expr.find[ type ]) ) {
                        // 通过寻找相邻元素来扩展context
                        if ( (seed = find(
                            token.matches[0].replace( runescape, funescape ),
                            rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
                        )) ) {
    
                            // 如果seed没有或者tokens里没有东西了,就可以直接返回了
                            tokens.splice( i, 1 );
                            selector = seed.length && toSelector( tokens );
                            if ( !selector ) {
                                push.apply( results, seed );
                                return results;
                            }
    
                            break;
                        }
                    }
                }
            }
        }
    
        // 编译并执行筛选函数
        // 提供match是为了防止再次执行tokenize(在我们已经对match更改过的情况下)
        compile( selector, match )(
            seed,
            context,
            !documentIsHTML,
            results,
            rsibling.test( selector ) && testContext( context.parentNode ) || context
        );
        return results;
    }

    可以看到对于简单的情况,select更改了下context,节省后面产生筛选函数后的闭包深度。对于真正的筛选工作,还是有compile编译得到的超级选择函数来做的。

    到这里,Sizzle里所有的大块函数基本都已经讲过了,下面就是在一个选择器传入后函数的运行步骤了。

    =======================================================================================

    首先是Sizzle暴露到外面的方法,几个主要的方法都在Sizzle闭包结束后运行,源码如下:

    jQuery.find = Sizzle;
    jQuery.expr = Sizzle.selectors;
    jQuery.expr[":"] = jQuery.expr.pseudos;
    jQuery.unique = Sizzle.uniqueSort;
    jQuery.text = Sizzle.getText;
    jQuery.isXMLDoc = Sizzle.isXML;
    jQuery.contains = Sizzle.contains;

    可以看到,jQuery.find完全是调用Sizzle来实现的,那么下面是普通的$('...')和$('...').find('...')形式,我们就要先重温下jQuery.fn.init中的代码片段了

                    // HANDLE: $(#id)
    // rootjQuery = jQuery( document );
    } else { elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; }
                // HANDLE: $(expr, $(...))
                } else if ( !context || context.jquery ) {
                    return ( context || rootjQuery ).find( selector );
    
                // HANDLE: $(expr, context)
                // (which is just equivalent to: $(context).find(expr)
                } else {
                    return this.constructor( context ).find( selector );
                }

    可以看到,对于选择器的处理,基本上除了ID以外,都使用了$(context).find(selector)形式,我们跟踪到find函数中(注意:此时的find函数是指在prototype中定义的find函数,而不是jQuery.find,两者所处的位置是有区别的),直接上find源码:

        find: function( selector ) {
            var i,
                ret = [],
                self = this,
                len = self.length;
    
            if ( typeof selector !== "string" ) {
                return this.pushStack( jQuery( selector ).filter(function() {
                    for ( i = 0; i < len; i++ ) {
                        if ( jQuery.contains( self[ i ], this ) ) {
                            return true;
                        }
                    }
                }) );
            }
    
            for ( i = 0; i < len; i++ ) {
                jQuery.find( selector, self[ i ], ret );
            }
    
            ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
            ret.selector = this.selector ? this.selector + " " + selector : selector;
            return ret;
        },

    在这个函数中,对于每一个context,都会执行一遍jQuery.find,即Sizzle,保证结果的齐全,同时在后面调用jQuery.unique来保证不重复。

    接下来就是进入Sizzle主函数了,Sizzle主函数在之前已经分析过了,就不具体讲了,主要的还是简单的选择器的话就直接处理返回,复杂的话就会调用select函数

    function Sizzle( selector, context, results, seed ) {
        var match, elem, m, nodeType,
            // QSA vars
            i, groups, old, nid, newContext, newSelector;
    
        if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
            setDocument( context );
        }
    
        context = context || document;
        results = results || [];
    
        if ( !selector || typeof selector !== "string" ) {
            return results;
        }
    
        if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
            return [];
        }
    
        if ( documentIsHTML && !seed ) {
    
            // Shortcuts
            if ( (match = rquickExpr.exec( selector )) ) {
                // Speed-up: Sizzle("#ID")
                if ( (m = match[1]) ) {
                    if ( nodeType === 9 ) {
                        elem = context.getElementById( m );
                        // Check parentNode to catch when Blackberry 4.6 returns
                        // nodes that are no longer in the document (jQuery #6963)
                        if ( elem && elem.parentNode ) {
                            // Handle the case where IE, Opera, and Webkit return items
                            // by name instead of ID
                            if ( elem.id === m ) {
                                results.push( elem );
                                return results;
                            }
                        } else {
                            return results;
                        }
                    } else {
                        // Context is not a document
                        if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
                            contains( context, elem ) && elem.id === m ) {
                            results.push( elem );
                            return results;
                        }
                    }
    
                // Speed-up: Sizzle("TAG")
                } else if ( match[2] ) {
                    push.apply( results, context.getElementsByTagName( selector ) );
                    return results;
    
                // Speed-up: Sizzle(".CLASS")
                } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
                    push.apply( results, context.getElementsByClassName( m ) );
                    return results;
                }
            }
    
            // QSA path
            if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
                nid = old = expando;
                newContext = context;
                newSelector = nodeType === 9 && selector;
    
                // qSA works strangely on Element-rooted queries
                // We can work around this by specifying an extra ID on the root
                // and working up from there (Thanks to Andrew Dupont for the technique)
                // IE 8 doesn't work on object elements
                if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
                    groups = tokenize( selector );
    
                    if ( (old = context.getAttribute("id")) ) {
                        nid = old.replace( rescape, "\$&" );
                    } else {
                        context.setAttribute( "id", nid );
                    }
                    nid = "[id='" + nid + "'] ";
    
                    i = groups.length;
                    while ( i-- ) {
                        groups[i] = nid + toSelector( groups[i] );
                    }
                    newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
                    newSelector = groups.join(",");
                }
    
                if ( newSelector ) {
                    try {
                        push.apply( results,
                            newContext.querySelectorAll( newSelector )
                        );
                        return results;
                    } catch(qsaError) {
                    } finally {
                        if ( !old ) {
                            context.removeAttribute("id");
                        }
                    }
                }
            }
        }
    
        // All others
        return select( selector.replace( rtrim, "$1" ), context, results, seed );
    }
    View Code

    select函数就对传入的selector进行简单的处理,再调用compile函数根据传入的selector和group来生成终极匹配函数,并立刻运行,来获取结果。

    这些具体的函数运行步骤的话,还是自己做个demo单步跟踪下比较清晰

    好了,jQuery中的Sizzle基本就这写了,剩下的一些小函数随便找点时间看下就懂了,都不讲了,后面的博客就会回到大的jQuery中了

  • 相关阅读:
    在html5中不支持<table>的cellpadding 和 cellspacing ; 2) 如何用css实现 cellpadding, cellspacing ; 3) tr , th 是 有 border, 没有 padding 的.
    css 实现 左右div 等高, 同时父级div就是最高的子div的高度.
    1)jquery validate 远程验证remote,自定义验证 , 手机号验证 2)bootstrap validate 远程remote验证的方法.
    点击图片或者鼠标放上hover .图片变大. 1)可以使用css中的transition, transform 2) 预先设置一个 弹出div. 3)弹出层 alert ; 4) 浏览器的宽度document.documentElement.clientWidth || document.body.clientWidth
    [上一个][下一个][上一节][下一节]
    点击li ,父辈出现; 子级,子辈不出现. prevUntil ---> 前面多个, 截止到 截止元素 , prev([expr]) --> 前面一个.
    通过 onclick = "test()"事件定义的事件 , 如何触发.
    在 子 iframe中 点击一个按钮, 变换 这个 iframe的地址url.
    ueditor 设置高度height. ue.setHeight(400); 设置宽度 width
    Yarn--集群资源管理器--总结
  • 原文地址:https://www.cnblogs.com/cyITtech/p/3676507.html
Copyright © 2011-2022 走看看