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选择器表达式exprSizzle.matchesSelector = function( elem, expr ) {...}
//被jQuery接管的部分APIjQuery.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)都支持该方法。
比较结果对应的描述如下
Bits | Number | Meaning |
000000 | 0 | 元素相同 |
000001 | 1 | 节点在不同的文档 |
000010 | 2 | docNode在docElem之前 |
000100 | 4 | docNode在docElem之后 |
001000 | 8 | docNode包含docElem |
010000 | 16 | docNode被docElem包含 |
100000 | 32 | 占位(浏览器私有使用) |
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 ) {varcompare;if ( a ===b ) {return 0;}if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition &&a.compareDocumentPosition( b )) ) { //如果b在a后面,那么compare的比特位至少是0?0100,?表示可能是0,也可能是1return 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 ) {returnsiblingCheck( 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 ) {varcur,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 ) {returnsiblingCheck( 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>
functionmyFunction()
{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 ) {varnode,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") {returnelem.textContent;}else{//遍历其子节点for ( elem = elem.firstChild; elem; elem =elem.nextSibling ) {ret+=getText( elem );}}//Text、CDATASection(不会由解析器解析的文本)} else if ( nodeType === 3 || nodeType === 4) {returnelem.nodeValue;}//不包括注释或处理指令节点returnret;
};
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 neededif ( ( 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上)节点会返回falseif ( ret || support.disconnectedMatch ||//同时,在IE9上,断开连接的节点被认为是一个文档片段elem.document && elem.document.nodeType !== 11) {returnret;}}catch(e) {}}return Sizzle( expr, document, null, [elem] ).length > 0;};
如果觉得本文还有那么一点点作用,请顶一下。
转载于:https://my.oschina.net/u/2258555/blog/624558
jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——一些有用的Sizzle API相关推荐
- 菜鸟读jQuery 2.0.3 源码分析系列(1)
原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...
- Kylin源码分析系列三—rowKey编码
Kylin源码分析系列三-rowKey编码 注:Kylin源码分析系列基于Kylin的2.5.0版本的源码,其他版本可以类比. 1. 相关概念 前面介绍了Kylin中Cube构建的流程,但Cube数据 ...
- vue源码分析系列三:render的执行过程和Virtual DOM的产生
render 手写 render 函数,仔细观察下面这段代码,试想一下这里的 createElement 参数是什么 . new Vue({el: '#application',render(crea ...
- Redis源码分析系列三:initServerConfig下半部分
2019独角兽企业重金招聘Python工程师标准>>> 经过短暂的休息,开始研究initServerConfig下半部分. //开始研究下半部分 /* Replication rel ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- [转载]jQuery1.6.1源码分析系列
转载:http://www.cnblogs.com/nuysoft/archive/2011/11/14/2248023.html [原创] jQuery1.6.1源码分析系列(停止更新) 作者:nu ...
- MyBatis 源码分析系列文章合集
1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...
- MyBatis 源码分析系列文章导读
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
最新文章
- Xen 和 KVM 下如何关闭 virbr0
- 开发日记-20190602 关键词 读书笔记《鸟哥的Linux私房菜-基础学习篇》
- uva 299 - Train Swapping
- 在线html表格设计器,6款优秀的在线表单设计器
- Java Review - PriorityQueue源码解读
- php和mysql的概述_PHP的MySQL扩展:MySQL数据库概述_MySQL
- Dynamics CRM 2013 初体验(1):系统的安装
- putty WinScp 免密登录远程 Linux
- php类如何变为静态调用,PHP类中静态方法如何调用非静态方法?_后端开发
- matlab在神经网络中的应用,应用matlab实现神经网络
- wireless 大作业 linux,Wire/Wireless Network Configuration in Linux
- 优秀在线答题小程序汇总分享
- 机器视觉软件能够做什么?-龙熙视觉机器视觉培训李杰
- 计算机拨号连接无法建立连接,电信拨号上网连接不上的解决方法
- 自动统计文件夹下所有音频时长与个数
- 从源数据库抽取数据到中间库
- java导出excel问题记录
- linux 卸载nexus,CentOS7安装Nexus
- 全球最厉害的14位程序员!你都认识谁?
- 游戏产品开发流程 - Leangoo项目管理工具
热门文章
- 疫情期间去澳洲的注意事项、签证持有者的限制信息
- Hbase原理加强篇
- HBASE原理及使用
- Unity3d网络游戏Socket通讯
- 网络安全行业至少还有20家上市公司的空间
- 有一个电影叫《原谅我,又一次撒谎》
- linux小工具之-ccat
- MFC 组合框的用法大全
- 已知一个带有表头结点的单链表,结点结构为 data link 假设该链表只给出了头指针list。在不改变链表的前提下,请设计一个尽可能高效的 算法,查找链表中倒数第k个位置
- 【统计学习系列】多元线性回归模型(六)——模型拟合质量评判:RMSE、R方、改进R方、AIC\BIC\SIC