jquery 源码分析八

在各种聚会,毕设任务后。。。我又回来了!!!

继续分析jquery中的sizzle源码

在Sizzle主函数以后,先是一些列的零散函数,为后面support检测,事件代理提供先期方法。这些函数内容比较简单,就直接分析掉了:

// 为function作标记
function markFunction( fn ) {
    fn[ expando ] = true;
    return fn;
}

/**
 * 用一个div元素来做一些检测
 */
function assert( fn ) {
    var div = document.createElement("div");

    try {
        return !!fn( div );
    } catch (e) {
        return false;
    } finally {
        // 将div移除
        if ( div.parentNode ) {
            div.parentNode.removeChild( div );
        }
        // 释放缓存
        div = null;
    }
}
// 给所有的attrs添加相同的handler
function addHandle( attrs, handler ) {
    var arr = attrs.split("|"),
        i = attrs.length;

    while ( i-- ) {
        Expr.attrHandle[ arr[i] ] = handler;
    }
}
/**
 * 检测两个元素的顺序
 * 返回小于0 则a在b前, 大于 0 则a在b后
 */
function siblingCheck( a, b ) {
    var cur = b && a,
        diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
            ( ~b.sourceIndex || MAX_NEGATIVE ) -
            ( ~a.sourceIndex || MAX_NEGATIVE );

    // 如果可以的话,就用IE的sourceIndex
    if ( diff ) {
        return diff;
    }

    // 检测b是否在a后面
    if ( cur ) {
        while ( (cur = cur.nextSibling) ) {
            if ( cur === b ) {
                return -1;
            }
        }
    }

    return a ? 1 : -1;
}
/**
 *会返回一个函数,用于检测input的type
 */
function createInputPseudo( type ) {
    return function( elem ) {
        var name = elem.nodeName.toLowerCase();
        return name === "input" && elem.type === type;
    };
}

/**
 * 返回一个函数,用于检测input或buttong的type
 */
function createButtonPseudo( type ) {
    return function( elem ) {
        var name = elem.nodeName.toLowerCase();
        return (name === "input" || name === "button") && elem.type === type;
    };
}

/**
 * 返回一个函数,用于检测元素的位置
 */
function createPositionalPseudo( fn ) {
    return markFunction(function( argument ) {
        argument = +argument;
        return markFunction(function( seed, matches ) {
            var j,
                matchIndexes = fn( [], seed.length, argument ),
                i = matchIndexes.length;

            // 找到特定位置的元素
            while ( i-- ) {
                if ( seed[ (j = matchIndexes[i]) ] ) {
                    seed[j] = !(matches[j] = seed[j]);
                }
            }
        });
    });
}

接下来就是setDocument了,它根据现有的document来设置,并在函数体中做了很多关于support的检测,它会返回当前的document

看了很久发现要写好多好多东西,这个函数居然有400行,容我慢慢注释下,后面会补上

 =========================================分割线========================================

好,下面上源码,和以前一样,里面调用的函数在下面会讲到,其中rbuggyMatches是支撑matches的正则串,rbuggyQSA是支撑qsa的正则串

setDocument = Sizzle.setDocument = function( node ) {
    var hasCompare,
        // 如果没有node,就直接去preferredDoc
        doc = node ? node.ownerDocument || node : preferredDoc,
        parent = doc.defaultView;

    // 如果没有document或者documentElement不可访问,就直接返回
    if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
        return document;
    }

    // 设置document
    document = doc;
    docElem = doc.documentElement;

    // 开始关于support的检测
    documentIsHTML = !isXML( doc );

    // Support: IE>8
    // 在iframe中,如果需要访问到document,在iframe重载之后
    // 当要访问document时,IE会报出"permission denied"错误
    // IE6-8 d不支持defaultView,所以parent会未定义
    if ( parent && parent !== parent.top ) {
        // IE11 不支持attachEvent。。。。
        if ( parent.addEventListener ) {
            parent.addEventListener( "unload", function() {
                setDocument();
            }, false );
        } else if ( parent.attachEvent ) {
            parent.attachEvent( "onunload", function() {
                setDocument();
            });
        }
    }

    /* 各种属性
    ---------------------------------------------------------------------- */

    // Support: IE<8
    // 检测getAttribute是否真的返回相应的attribute而不是属性
    support.attributes = assert(function( div ) {
        div.className = "i";
        return !div.getAttribute("className");
    });

    /* getElement(s)By*
    ---------------------------------------------------------------------- */

    // 检测 getElementsByTagName("*") 是否只返回元素
    support.getElementsByTagName = assert(function( div ) {
        div.appendChild( doc.createComment("") );
        return !div.getElementsByTagName("*").length;
    });

    // 检测是否getElementsByClassName是否工作正常
    support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
        div.innerHTML = "<div class='a'></div><div class='a i'></div>";

        div.firstChild.className = "i";
        // Support: Opera<10
        // 判断是否可以得到非开头的class
        return div.getElementsByClassName("i").length === 2;
    });

    // Support: IE<10
    // 检测getElementById 返回的是不是 elements by name
    support.getById = assert(function( div ) {
        docElem.appendChild( div ).id = expando;
        return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
    });

    // ID find and filter
    if ( support.getById ) {
        Expr.find["ID"] = function( id, context ) {
            if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
                var m = context.getElementById( id );
                // 检测是否返回了已经删除了的元素
                return m && m.parentNode ? [m] : [];
            }
        };
        Expr.filter["ID"] = function( id ) {
            var attrId = id.replace( runescape, funescape );
            return function( elem ) {
                return elem.getAttribute("id") === attrId;
            };
        };
    } else {
        // Support: IE6/7
        // getElementById并不可靠
        delete Expr.find["ID"];

        Expr.filter["ID"] =  function( id ) {
            var attrId = id.replace( runescape, funescape );
            return function( elem ) {
                var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
                return node && node.value === attrId;
            };
        };
    }

    // Tag
    Expr.find["TAG"] = support.getElementsByTagName ?
        function( tag, context ) {
            if ( typeof context.getElementsByTagName !== strundefined ) {
                return context.getElementsByTagName( tag );
            }
        } :
        function( tag, context ) {
            var elem,
                tmp = [],
                i = 0,
                results = context.getElementsByTagName( tag );

            // 将不是element的元素剔除
            if ( tag === "*" ) {
                while ( (elem = results[i++]) ) {
                    if ( elem.nodeType === 1 ) {
                        tmp.push( elem );
                    }
                }

                return tmp;
            }
            return results;
        };

    // Class
    Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
        if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
            return context.getElementsByClassName( className );
        }
    };

    /* QSA/matchesSelector
    ---------------------------------------------------------------------- */

    // QSA and matchesSelector support

    // matchesSelector(:active) 在应该是true时会返回false (IE9/Opera 11.5)
    rbuggyMatches = [];

    // qSa(:focus) 在应该是true时会返回false (Chrome 21)
    rbuggyQSA = [];

    if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
        assert(function( div ) {
            // Select 被有意的设置为空
            // 这是为了检测IE的行为
            div.innerHTML = "<select t=''><option selected=''></option></select>";

            // Support: IE8, Opera 10-12
            // 在^= 或 $= 或 *=之后如果字符串为空,应该无元素返回
            if ( div.querySelectorAll("[t^='']").length ) {
                rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|"")" );
            }

            // Support: IE8
            // bool属性并没有被正确的使用
            if ( !div.querySelectorAll("[selected]").length ) {
                rbuggyQSA.push( "\[" + whitespace + "*(?:value|" + booleans + ")" );
            }

            // Webkit/Opera - :checked 应该返回被选中的元素
            if ( !div.querySelectorAll(":checked").length ) {
                rbuggyQSA.push(":checked");
            }
        });

        assert(function( div ) {
            // Support: Windows 8 Native Apps
            // 在设置innerHTML时,type和name的设置会被限制
            var input = doc.createElement("input");
            input.setAttribute( "type", "hidden" );
            div.appendChild( input ).setAttribute( "name", "D" );

            // Support: IE8
            // 增强对name的敏感程度
            if ( div.querySelectorAll("[name=d]").length ) {
                rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
            }

            // FF 3.5 - :enabled/:disabled ,hidden 元素 (hidden元素仍然是enable状态)
            if ( !div.querySelectorAll(":enabled").length ) {
                rbuggyQSA.push( ":enabled", ":disabled" );
            }

            // Opera 10-11 在不正确的查询下不会报错
            div.querySelectorAll("*,:x");
            rbuggyQSA.push(",.*:");
        });
    }

    if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
        docElem.mozMatchesSelector ||
        docElem.oMatchesSelector ||
        docElem.msMatchesSelector) )) ) {

        assert(function( div ) {
            // 首先尝试下matchesSelector是否能够正常运行
            support.disconnectedMatch = matches.call( div, "div" );

            // 这个应该会报错
            // 在Gecko无错误,只会返回一个false
            matches.call( div, "[s!='']:x" );
            rbuggyMatches.push( "!=", pseudos );
        });
    }

    rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
    rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );

    /* Contains
    ---------------------------------------------------------------------- */
    hasCompare = rnative.test( docElem.compareDocumentPosition );

    // 元素包含
    // 这个函数返回的值里,一个元素不会被自己包含
    contains = hasCompare || rnative.test( docElem.contains ) ?
        function( a, b ) {
            var adown = a.nodeType === 9 ? a.documentElement : a,
                bup = b && b.parentNode;
            return a === bup || !!( bup && bup.nodeType === 1 && (
                adown.contains ?
                    //直接用原生的contains
                    adown.contains( bup ) :
                    //或者使用compareDocumentPosition,关于&16的问题可以看下
                    //关于这个compareDocumentPosition函数的返回值
                    a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
            ));
        } :
        function( a, b ) {
            if ( b ) {
                // 循环取b的parentNode,取到顶后会返回false
                while ( (b = b.parentNode) ) {
                    if ( b === a ) {
                        return true;
                    }
                }
            }
            return false;
        };

    /* 排序
    ---------------------------------------------------------------------- */

    // Document order sorting
    sortOrder = hasCompare ?
    function( a, b ) {

        // Flag for duplicate removal
        if ( a === b ) {
            hasDuplicate = true;
            return 0;
        }

        // 在a,b中只有一个有compareDocumentPosition的情况下运行,直接返回
        var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
        if ( compare ) {
            return compare;
        }

        // 在他们都属于一个文档下,调用compareDocumentPosition
        compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
            a.compareDocumentPosition( b ) :

            // 否则他们无关
            1;

        // 对于无关的两个node
        if ( compare & 1 ||
            // support.sortDetached对于Webkit<537.32 - Safari 6.0.3/Chrome 25的bug检测
            (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {

            // 选择第一个和现在文档相关的元素
            if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
                return -1;
            }
            if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
                return 1;
            }

            // 用原有的order,这个还有点迷惑,sortInput貌似只在一个函数里用过,而且是立即销毁的
            return sortInput ?
                ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
                0;
        }

        return compare & 4 ? -1 : 1;
    } :
    function( a, b ) {
        // 元素相同就返回
        if ( a === b ) {
            hasDuplicate = true;
            return 0;
        }

        var cur,
            i = 0,
            aup = a.parentNode,
            bup = b.parentNode,
            ap = [ a ],
            bp = [ b ];

        // 如果无parent,那么不是document,就是无关的
        if ( !aup || !bup ) {
            return a === doc ? -1 :
                b === doc ? 1 :
                aup ? -1 :
                bup ? 1 :
                sortInput ?
                ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
                0;

        // 如果两个是sibling关系的,就直接调用siblingCheck
        } else if ( aup === bup ) {
            return siblingCheck( a, b );
        }

        // 否则就需要完全列出它们的祖先元素
        cur = a;
        while ( (cur = cur.parentNode) ) {
            ap.unshift( cur );
        }
        cur = b;
        while ( (cur = cur.parentNode) ) {
            bp.unshift( cur );
        }

        // 向下遍历直到找出分支
        while ( ap[i] === bp[i] ) {
            i++;
        }

        return i ?
            // 看下是否有正常的祖先元素
            siblingCheck( ap[i], bp[i] ) :

            // 否则节点在document外??
            ap[i] === preferredDoc ? -1 :
            bp[i] === preferredDoc ? 1 :
            0;
    };

    return doc;
};

接下来是其中的几个小函数调用,首先是support.sortDetached:

// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25
// 侦测node对于follow混淆
support.sortDetached = assert(function( div1 ) {
    // 应该返回1,但是返回的是4
    return div1.compareDocumentPosition( document.createElement("div") ) & 1;
});

然后是siblingCheck,这个函数在这篇文章的前面分析过~

这篇就到这吧,回去搞我的毕业设计了。。。。。

原文地址:https://www.cnblogs.com/cyITtech/p/3635088.html