jQuery选择器源码解读(一):Sizzle方法

前端之家收集整理的这篇文章主要介绍了jQuery选择器源码解读(一):Sizzle方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

对jQuery的Sizzle各方法做了深入分析(同时也参考了一些网上资料)后,将结果分享给大家。我将采用连载的方式,对Sizzle使用的一些方法详细解释一下,每篇文章介绍一个方法

若需要转载,请写明出处,多谢。

方法是Sizzle选择器包的主要入口,jQuery的find方法就是调用方法获取匹配的节点 * 该方法主要完成下列任务: * 1、对于单一选择器,且是ID、Tag、Class三种类型之一,则直接获取并返回结果 * 2、对于支持querySelectorAll方法的浏览器,通过执行querySelectorAll方法获取并返回匹配的DOM元素 * 3、除上之外则调用select方法获取并返回匹配的DOM元素 * * * @param selector 选择器字符串 * @param context 执行匹配的最初的上下文(即DOM元素集合)。若context没有赋值,则取document。 * @param results 已匹配出的部分最终结果。若results没有赋值,则赋予空数组。 * @param seed 初始集合 */ function Sizzle(selector,context,results,seed) { var match,elem,m,nodeType,// QSA vars i,groups,old,nid,newContext,newSelector;
/*
 * preferredDoc = window.document
 * 
 * setDocument<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>完成一些初始化工作
 */
if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
    setDocument(context);
}

context = context || document;
results = results || [];

/*
 * 若selector不是有效地字符串类型数据,则直接返回results
 */
if (!selector || typeof selector !== "string") {
    return results;
}

/*
 * 若context既不是document(nodeType=9),也不是element(nodeType=1),那么就返回空集合
 */
if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {
    return [];
}

// 若当前过滤的是HTML文档,且没有设定seed,则执行if内的语句体
if (documentIsHTML && !seed) {

    /* 
     * 若选择器是单一选择器,且是ID、Tag、Class三种类型之一,则直接<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>并返回结果
     * 
     * rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
     * 上述正则表达式括号内三段依次分别用来判断是否是ID、TAG、CLASS类型的单一选择器
     * 上述正则表达式在最外层圆括号内有三个子表达式(即三个圆括号括起来的部分),
     *   分别代表ID、Tag、Class选择器的值,在下面<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>中,分别体现在match[1]、match[2]、match[3]
     */
    if ((match = rquickExpr.exec(selector))) {
        // Speed-up: Sizzle("#ID")
        // 处理ID类型选择器,如:#ID
        if ((m = match[1])) {
            // 若当前上下文是一个document,则执行if内语句体
            if (nodeType === 9) {
                elem = context.getElementById(m);
                // 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,Opera,and Webkit
// return items
// by name instead of ID
/*

  • 一些老版本的浏览器会把name当作ID来处理,

  • 返回不正确的结果,所以需要再一次对比返回节点的ID属性
    /
    if (elem.id === m) {
    results.push(elem);
    return results;
    }
    } else {
    return results;
    }
    } else {
    // Context is not a document
    /

    • contains(context,elem)用来确认获取的elem是否是当前context对象的子对象
      */
      if (context.ownerDocument
      && (elem = context.ownerDocument.getElementById(m))
      && contains(context,elem) && elem.id === m) {
      results.push(elem);
      return results;
      }
      }
             // Speed-up: Sizzle("TAG")
             // 处理Tag类型选择器,如:SPAN
         } else if (match[2]) {
             push.apply(results,context.getElementsByTagName(selector));
             return results;
    
             // Speed-up: Sizzle(".CLASS")
             /*
              * 处理class类型选择器,如:.class
              * 下面<a href="https://www.jb51.cc/tag/tiaojianpanduan/" target="_blank" class="keywords">条件判断</a>分别是:
              * m = match[3]:有效的class类型选择器
              * support.getElementsByClassName 该选择器的div<a href="https://www.jb51.cc/tag/zhichi/" target="_blank" class="keywords">支持</a>getElementsByClassName
              * context.getElementsByClassName 当前上下文节点有getElementsByClassName<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>
              * 
              */ 
    
         } else if ((m = match[3]) && support.getElementsByClassName
                 && context.getElementsByClassName) {
             push.apply(results,context.getElementsByClassName(m));
             return results;
         }
     }
    
     // QSA path
     /*
      * 若浏览器<a href="https://www.jb51.cc/tag/zhichi/" target="_blank" class="keywords">支持</a>querySelectorAll<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>且选择器符合querySelectorAll<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>标准,则执行if内语句体
      * 在这里的检查仅仅是简单匹配
      * 第一次<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>Sizzle时,rbuggyQSA为空
      * 
      * if语句体内对当前context对象的id的赋值与恢复,是用来修正querySelectorAll的一个BUG
      * 该BUG会在某些情况下把当前节点(context)也作为结果返回回来。
      * 具体<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>是,在现有的选择器前<a href="https://www.jb51.cc/tag/jiashang/" target="_blank" class="keywords">加上</a>一个<a href="https://www.jb51.cc/tag/shuxing/" target="_blank" class="keywords">属性</a>选择器:[id=XXX],
      * XXX 为context的id,若context本身没有设置id,则给个默认值expando。
      */
    
     if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
         nid = old = expando;
         newContext = context;
         // 若context是document,则newSelector取自selector,否则为false
         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"))) {
                 /*
                  * rescape = /'|\\/g,* 这里将old中的单引号、竖杠、反斜杠前加一个反斜杠
                  * old.replace(rescape,"\\$&")<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>中的$&代表匹配项
                  */
                 nid = old.replace(rescape,"\\$&");
             } else {
                 context.setAttribute("id",nid);
             }
             nid = "[id='" + nid + "'] ";
    
             // 重新组合新的选择器
             i = groups.length;
             while (i--) {
                 groups[i] = nid + toSelector(groups[i]);
             }
             /*
              * rsibling = new RegExp(whitespace + "*[+~]")
              * rsibling用于判定选择器是否存在兄弟关系符
              * 若包含+~符号,则取context的父节点作为当前节点
              */
             newContext = rsibling.test(selector) && context.parentNode

    || context;
    newSelector = groups.join(",");
    }

         if (newSelector) {
             /*
              * 这里之所以需要用try...catch,
              * 是因为jquery所<a href="https://www.jb51.cc/tag/zhichi/" target="_blank" class="keywords">支持</a>的一些选择器是querySelectorAll所<a href="https://www.jb51.cc/tag/buzhichi/" target="_blank" class="keywords">不支持</a>的,
              * 当使用这些选择器时,querySelectorAll会报非法选择器,
              * 故需要jquery自身去实现。
              */
             try {
                 // 将querySelectorAll<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>的结果并入results,而后返回resulsts
                 push.apply(results,newContext

    .querySelectorAll(newSelector));
    return results;
    } catch (qSAError) {
    } finally {
    if (!old) {
    context.removeAttribute("id");
    }
    }
    }
    }
    }

    // All others
    // 除上述快捷方式和调用querySelectorAll方式直接获取结果外,其余都需调用select来获取结果
    /*

    • rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\])(?:\\.)*)"
      • whitespace + "+$","g"),* whitespace = "[\x20\t\r\n\f]";
    • 上述rtrim正则表达式的作用是去掉selector两边的空白,空白字符由whitespace变量定义
    • rtrim的效果与new RegExp("^" + whitespace + "+|" + whitespace + "+$","g")相似
      */
      return select(selector.replace(rtrim,"$1"),seed);
      }

各位朋友,若觉得写得不错,帮我顶一下,给点动力,多谢!

原文链接:https://www.f2er.com/jquery/55045.html

猜你在找的jQuery相关文章