jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——一些有用的Sizzle API

说一下Sizzle中零碎的API。这些API有的被jQuery接管,直接使用jQuery.xxx就可以使用,有的没有被接管,如果要在jQuery中使用,使用方法是jQuery.find.xxx。

  具体看一下有哪些API

//筛选出elements满足CSS选择器表达式expr的节点【最终返回的是节点数组】
Sizzle.matches = function( expr, elements ) {...}

//判断dom元素elem是否匹配CSS选择器表达式expr
Sizzle.matchesSelector = function( elem, expr ) {...}


//被jQuery接管的部分API
jQuery.find = Sizzle;//查找函数 jQuery.unique = Sizzle.uniqueSort;//根据DOM元素在文档中出现的先后顺序对DOM元素数组进行排序,并移除重复的元素 jQuery.text = Sizzle.getText;//获取节点elem下的所有文本内容【包括标签之间的空白】 jQuery.isXMLDoc = Sizzle.isXML;//判断DOM节点是否位于XML文档中,或者其本身就是XML文档 jQuery.contains = Sizzle.contains;//用于判断指定元素内是否包含另一个元素

  接下来一一分析(jQuery.find就不分析了)。

 

Sizzle.contains(forefather,posterity):用于判断指定元素内是否包含另一个元素

  通俗的来讲,即是判断某一个DOM节点posterity是否是forefather后代节点

  源码比较简单,处理过程是如果能通过浏览器自带的docElem.contains或docElem.compareDocumentPosition来处理则使用之;否则一直查找posterity节点的parentNode和forefather节点比较,如果找到和forefather相同的节点则返回true,否则返回false。

  所以,这里关键的地方是浏览器的原生函数docElem.contains或docElem.compareDocumentPosition。

  docElem.contains(docNode):判断节点docNode是否包含在docElem中。【需要注意:当docNode和docElem是同一个节点的时候也返回true。这是和Sizzle.contains不同的地方】。这个方法并没有标准化,期初用于IE,但是现代的浏览器都实现了这个功能。

  docElem.compareDocumentPosition(docNode):返回一个比特码用于确定docElem和docNode两个节点之间的位置关系。这个函数是DOM3标准的一部分。现代浏览器(IE9+,firefox)都支持该方法。

  比较结果对应的描述如下

BitsNumber        Meaning 
0000000元素相同
0000011节点在不同的文档
0000102docNode在docElem之前
0001004docNode在docElem之后
0010008docNode包含docElem
01000016docNode被docElem包含
10000032占位(浏览器私有使用)

  docElem.compareDocumentPosition(docNode)的得到的结果应当是符合的比特位相加

  比如:

    <div id="demo">
      <div category="children"><span></span></div>
      <div category="cooking"></div>
      <div category="web" cover="paperback"></div>
      <div category="web"></div>
    </div>

  js:

var xmlDoc = document.getElementById('demo');
var a=xmlDoc.getElementsByTagName('p')[0]; var b=xmlDoc.getElementsByTagName('span')[0]; document.write("<br> Number of compareDocumentPosition: " +a.compareDocumentPosition(b));

  首先b在a后面,所以符合这个条件的比特值是4;其次b被a包含,符合这个条件的比特值为16。a.compareDocumentPosition(b)的最终结果是4 + 16 = 20,比特值为010100;考虑各种浏览器的兼容情况,所以a.compareDocumentPosition( bup ) & 16的结果就是a是否包含bup的结果。

 

  说到比较节点位置关系需要知道一些东东。XML 经常在节点之间含有换行或空白字符。这是在使用简单的编辑器(比如记事本)时经常出现的情况。比如下面的例子

  现代浏览器(Firefox,chrome,IE9+)会把空的空白或换行作为文本节点来处理

var xmlDoc = document.getElementById('demo');  
var x=xmlDoc.childNodes; document.write("Number of child nodes: " + x.length);//Number of child nodes: 9

  但是IE8-浏览器不会这样,浏览器会忽略元素节点之间的空文本节点。这个时候获取到的节点数量不是为9,而是4。

  比较好的状况是docElem.compareDocumentPosition只有现代浏览器才支持,所以在支持docElem.compareDocumentPosition的情况,各个浏览器比较结果都应该是相同的。

 

Sizzle.uniqueSort(domArray):根据DOM元素在文档中出现的先后顺序对DOM元素数组进行排序,并移除重复的元素

   参数domArray只能是DOM元素数组。并且重复的元素指的是同一个节点(使用“===”比较)。

  去重不是难点。在去重之前需要对节点进行排序。使用数组的sort方法。

  

  这里讲一讲sort的使用:

  语法:arrayObject.sort(sortby)

  参数:sortby必须是函数,用来规定排序顺序,可选

  返回值:对数组(原数组,不生成副本)的引用。

  说明:如果没有使用参数,将按字母顺序对数组中的元素进行排序。内部实现——应把数组的元素都转换成字符串(如有必要)来进行比较。如果提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。

  比较函数应该具有两个参数 a 和 b,其返回值如下:

  • 若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值,即a-b应当为负值。
  • 若 a 等于 b,则返回 0。
  • 若 a 大于 b,在排序后的数组中 a 应该出现在 b 之后,则返回一个大于 0 的值,即a - b为正值。

  

  明白了排序函数以后,我们明白,因为参数是DOM节点数组,不可能使用默认的排序方式,我们必须自定义比较函数sorby。

  前面我们分析了浏览器原生方法docElem.compareDocumentPosition,这个函数就是用来判断节点关系的最好方法了。如果能使用这个函数,我们的比较函数可以是

function( a, b ) {
    var compare;

    if ( a === b ) {return 0;
    }

    if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) {
    //如果b在a后面,那么compare的比特位至少是0?0100,?表示可能是0,也可能是1
return compare & 4 ? -1 : 1; }   //最后的容错处理,如果节点a不包含compareDocumentPosition方法,我们认为是非法节点,直接放在数组最后。 return a.compareDocumentPosition ? -1 : 1; }

  但是有一个中特殊情况:a和b不再同一个文档内。那么就看a、b节点哪个不在当前文档内那么哪个节点就应该放在最后。这部分的判断如下

        if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) {
       //a就是当前文档或是在window.document中,则需将b放在数组最后
if ( a === doc || contains( preferredDoc, a ) ) { return -1; }
       //b就是当前文档或是在window.document中,则需要将a放在数组最后
if ( b === doc || contains( preferredDoc, b ) ) { return 1; } return 0; }

  

  如果不能使用docElem.compareDocumentPosition,判断就稍微复杂一些。但是也可以做一些快速判断,比如相同的节点、互为兄弟节点、某个节点是document或节点已经失去连接,这些情况可以先做判断

                if ( a === b ) {
                    hasDuplicate = true;
                    return 0;

                // 没有父母的节点或者是document节点或断开连接的节点(失联的节点没有parentNode)
                } else if ( !aup || !bup ) {
                    return a === doc ? -1 :
                    b === doc ? 1 :
                    aup ? -1 :
                    bup ? 1 :
                    0;

                    // 如果是兄弟节点,则快速检测
                } else if ( aup === bup ) {
                    return siblingCheck( a, b );
                }

  其他情况只能是从DOM的根节点开始判断a和b属于那个分支,比较分支的先后即可。这里面jQuery做的非常巧妙,将a和b的祖先节点分别压入ap和bp。注意压入顺序是将越是靠近根节点的祖先节点放在数组的最前面。

         // 否则,我们需要他们的祖先比较完整列表
                cur = a;
                while ( (cur = cur.parentNode) ) {
                    ap.unshift( cur );
                }
                cur = b;
                while ( (cur = cur.parentNode) ) {
                    bp.unshift( cur );
                }

  结果形成的ap/bp为如下

  ap = [#document节点,html节点,body节点,...]

  bp = [#document节点,html节点,body节点,...]

  从ap和bp的前半部分都是相同的,从某个下标index开始,ap[index]和bp[index]不同。很明显,ap[index]和bp[index]是兄弟节点,我们只需要比较ap[index]和bp[index]的谁在前,那么a和b中谁就在前。那么,判断的代码应该如下  

                // 从树根节点开始往下找差异
                while ( ap[i] === bp[i] ) {
                    i++;
                }

                return i ?
                    // 如果节点有一个共同的祖先,做一个同级检查
                    siblingCheck( ap[i], bp[i] ) :0;

  但是这里还存在一个问题:如果a或b不再文档内呢?所以这里还要加上这个部分的判断代码。所以最终这个对比函数的源码为

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

    if ( a === b ) {
        hasDuplicate = true;
        return 0;

    // 没有父母的节点或者是document节点或断开连接的节点(失联的节点没有parentNode)
    } else if ( !aup || !bup ) {
        return a === doc ? -1 :
        b === doc ? 1 :
        aup ? -1 :
        bup ? 1 :
        0;

        // 如果是兄弟节点,则快速检测
    } 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] ) :

        // 否则节点在我们的文档内的排在前面
        ap[i] === preferredDoc ? -1 :
        bp[i] === preferredDoc ? 1 :
        0;
}

 

Sizzle.getText(elem):获取节点elem下的所有文本内容【包括标签之间的空白】

  参数elem可以是一个节点,也可以是及节点数组。

  这部分代码比较简单。主要注意的是兼容性问题。docElem.textContent获取内容是正确的,但是并非所有的浏览器都支持该方法,比如IE8-。而IE支持的innerText方法有问题:该方法会自动给两个标签之间加空格。

  比如  

<ul id="myList"><li id="item1">Coffee<div>sdddddddddd</div></li><li id="item2">Tea</li></ul>
<script>
function myFunction()
{
  var lst = document.getElementById("myList");
  var x = lst.textContent ;//CoffeesddddddddddTea
  var y =lst.innerText;//Coffee sdddddddddd Tea }
myFunction();
</script>

  所以,不能使用innerText来替代。没办法,IE8-只能使用文本节点的docElem.nodeValue方法获取单个文本节点了:将elem下所有文本节点的nodeValue相加。

  完整源码如下:  

getText = Sizzle.getText = function( elem ) {
     var node,
     ret = "",
     i = 0,
     nodeType = elem.nodeType;

     if ( !nodeType ) {
        // 如果没有nodeType, 这预计是一个数组
        for ( ; (node = elem[i]); i++ ) {
            // 不经过注释节点
            ret += getText( node );
        }
    //element、document、DocumentFragment节点
    } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
        // 使用textContent来获取
        // 使用innerText属性删除了新线的一致性 (see #11153)
        if ( typeof elem.textContent === "string" ) {
            return elem.textContent;
        } else {
            // 遍历其子节点
            for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
                ret += getText( elem );
            }
        }
    //Text、CDATASection(不会由解析器解析的文本)
    } else if ( nodeType === 3 || nodeType === 4 ) {
        return elem.nodeValue;
    }
    //不包括注释或处理指令节点

    return ret;
};

 

Sizzle.isXML(elem):判断DOM节点是否位于XML文档中,或者其本身就是XML文档

  该函数主要用于判断指定文档是一个XML文档还是一个HTML(或XHTML)文档。这个判断比较简单,直接附上源码

     Sizzle.isXML = function( elem ) {
            // documentElement的判断方式在他不存在的时候是可靠的
            // (如在IE浏览器加载内置iframe- #4833)
            var documentElement = elem && (elem.ownerDocument || elem).documentElement;
            return documentElement ? documentElement.nodeName !== "HTML" : false;
        };

 

Sizzle.matches( expr, elements):筛选出elements满足CSS选择器表达式expr的节点【最终返回的是节点数组】。

  实现比较简单啦,上源码

        Sizzle.matches = function( expr, elements ) {
            return Sizzle( expr, null, null, elements );
        };

 

Sizzle.matchesSelector(elem, expr):判断dom元素elem是否匹配CSS选择器表达式expr

  现代浏览器(IE9+,firefox,chrome等)都支持原生的docElem.matchesSelector,只不过带上来各自对前缀。OK哪就简单了,尽量使用浏览器原生的方法,如果不行再使用Sizzle()方法从备选种子elem中获取满足表达式expr的结果来判断。需要注意的是IE9虽然也支持msMatchesSelector,但是在失联的节点上会返回false,所以此时也使用Sizzle来处理。源码如下

        Sizzle.matchesSelector = function( elem, expr ) {
            // Set document vars if needed
            if ( ( elem.ownerDocument || elem ) !== document ) {
                setDocument( elem );
            }

            //rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g
            // 确保属性选择器正确
            expr = expr.replace( rattributeQuotes, "='$1']" );

            //rbuggyMatches = /(?:)/
            //rbuggyQSA总是包含 :focus,所以没有必要做存在确认
            if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
                try {
                    var ret = matches.call( elem, expr );

                    // IE9 matchesSelector在断开连接(不再document上)节点会返回false
                    if ( ret || support.disconnectedMatch ||
                            //同时,在IE9上,断开连接的节点被认为是一个文档片段
                            elem.document && elem.document.nodeType !== 11 ) {
                        return ret;
                    }
                } catch(e) {}
            }

            return Sizzle( expr, document, null, [elem] ).length > 0;
        };

  

  如果觉得本文还有那么一点点作用,请顶一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要下载 jQuery 1.9.1.min.js,你可以按照以下步骤进行操作: 首先,打开 jQuery 官方网站。在浏览器的地址栏输入"jquery.com",然后按下回车键。 点击页面上的 "Download"(下载)选项。这将会导航至 jQuery 下载页面。 在下载页面中,你可以看到一个包含多个可选择版本的列表。确保选择的是 "1.9.1" 版本。 在 "1.9.1" 版本下面,你会找到 "Download the compressed, production jQuery 1.9.1" 链接。点击该链接。 随即,一个弹出窗口将询问你是否保存或下载文件。选择 "保存文件" 选项,并选择一个合适的文件夹。 保存完毕后,你可以在选择的文件夹中找到并使用 "jquery-1.9.1.min.js" 文件。 现在,你已成功下载了 jQuery 1.9.1.min.js 文件,你可以在你的项目中引入该文件并开始使用 jQuery 的功能了。 ### 回答2: 要下载 jQuery 1.9.1 版本的压缩文件 `jquery-1.9.1.min.js`,可以通过以下步骤进行操作: 1. 首先,在浏览器中打开 jQuery 官方网站(https://jquery.com/)。 2. 在网站的顶部导航菜单中,找到 "Download"(下载)选项,点击进入下载页面。 3. 在下载页面中,你会看到多个版本的 jQuery 可供选择。找到并选择 "1.x" 系列版本,然后点击该系列下的 "Download the compressed, production jQuery 1.9.1"(下载已压缩的 jQuery 1.9.1)链接。 4. 开始下载后,浏览器会提示保存文件的位置和名称。你可以选择保存到自己想要的目录,并将文件名设置为 "jquery-1.9.1.min.js"。 5. 确认保存位置和文件名后,点击 "保存",文件下载就会开始。 下载完成后,你就可以在你选择的目录中找到 "jquery-1.9.1.min.js" 文件了。这个文件是已经压缩过的 jQuery 1.9.1 版本的 JavaScript 文件,你可以将其引入到你的网页中使用 jQuery 的功能。 ### 回答3: jQuery-1.9.1.min.js 是一个 JavaScript 库,用于简化网页开发中常见的操作和任务。这个文件可以通过多种方式进行下载。 首先,可以通过 jQuery 官方网站(https://jquery.com/download/)来下载 jQuery-1.9.1.min.js 文件。在网站上,你可以找到各种不同版本的 jQuery 文件,包括被压缩(minified)的版本和未压缩的版本。选择需要的版本后,点击下载按钮即可下载。 如果你使用的是包管理工具,比如 npm 或者 Yarn,你也可以通过命令行界面来下载 jQuery 文件。打开命令行界面,进入你的项目目录,然后运行适当的命令来下载文件。例如,在使用 npm 的情况下,可以运行命令:```npm install jquery@1.9.1``` 。这样可以将 jQuery-1.9.1.min.js 文件下载到你的项目目录下的 node_modules 文件夹中。 此外,你还可以从第方网站下载 jQuery-1.9.1.min.js 文件。有很多网站提供 jQuery 文件的下载,包括一些开发工具和资源网站。通过搜索引擎搜索 "jQuery-1.9.1.min.js 下载",你可以找到很多下载链接。请注意,从第方网站下载文件时,务必选择可信赖的来源,以避免下载到不安全或被修改过的文件。 总而言之,下载 jQuery-1.9.1.min.js 文件的方式有多种,你可以通过 jQuery 官网、包管理工具或者第方网站来获取该文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值