jquery Selector 源码分析

  1. /**
  2. * author:prk
  3. * date:2008-08-04
  4. * comment:comment for selector of jQuery
  5. *
  6. */
  7. var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417
  8. ? "(?:[\\w*_-]|\\\\.)"
  9. : "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)", quickChild = new RegExp("^>\\s*("
  10. + chars + "+)"), quickID = new RegExp("^(" + chars + "+)(#)(" + chars
  11. + "+)"), // ^((?:[\\w*_-]|\\\\.))(#)((?:[\\w*_-]|\\\\.))
  12. quickClass = new RegExp("^([#.]?)(" + chars + "*)");// ^([#.]?)((?:[\\w*_-]|\\\\.)*)
  13. jQuery.extend( {
  14. expr : {
  15. "" : function(a, i, m) {
  16. return m[2] == "*" || jQuery.nodeName(a, m[2]);
  17. },
  18. "#" : function(a, i, m) {
  19. return a.getAttribute("id") == m[2];
  20. },
  21. ":" : {
  22. // Position Checks
  23. lt : function(a, i, m) {
  24. return i < m[3] - 0;
  25. },
  26. gt : function(a, i, m) {
  27. return i > m[3] - 0;
  28. },
  29. nth : function(a, i, m) {
  30. return m[3] - 0 == i;
  31. },
  32. eq : function(a, i, m) {
  33. return m[3] - 0 == i;
  34. },
  35. first : function(a, i) {
  36. return i == 0;
  37. },
  38. last : function(a, i, m, r) {
  39. return i == r.length - 1;
  40. },
  41. even : function(a, i) {
  42. return i % 2 == 0;
  43. },
  44. odd : function(a, i) {
  45. return i % 2;
  46. },
  47. // Child Checks
  48. "first-child" : function(a) {
  49. return a.parentNode.getElementsByTagName("*")[0] == a;
  50. },
  51. "last-child" : function(a) {
  52. return jQuery.nth(a.parentNode.lastChild, 1, "previousSibling") == a;
  53. },
  54. "only-child" : function(a) {
  55. return !jQuery.nth(a.parentNode.lastChild, 2, "previousSibling");
  56. },
  57. // Parent Checks
  58. parent : function(a) {
  59. return a.firstChild;
  60. },
  61. empty : function(a) {
  62. return !a.firstChild;
  63. },
  64. // Text Check
  65. contains : function(a, i, m) {
  66. return (a.textContent || a.innerText || jQuery(a).text() || "")
  67. .indexOf(m[3]) >= 0;
  68. },
  69. // Visibility
  70. visible : function(a) {
  71. return "hidden" != a.type && jQuery.css(a, "display") != "none"
  72. && jQuery.css(a, "visibility") != "hidden";
  73. },
  74. hidden : function(a) {
  75. return "hidden" == a.type || jQuery.css(a, "display") == "none"
  76. || jQuery.css(a, "visibility") == "hidden";
  77. },
  78. // Form attributes
  79. enabled : function(a) {
  80. return !a.disabled;
  81. },
  82. disabled : function(a) {
  83. return a.disabled;
  84. },
  85. checked : function(a) {
  86. return a.checked;
  87. },
  88. selected : function(a) {
  89. return a.selected || jQuery.attr(a, "selected");
  90. },
  91. // Form elements
  92. text : function(a) {
  93. return "text" == a.type;
  94. },
  95. radio : function(a) {
  96. return "radio" == a.type;
  97. },
  98. checkbox : function(a) {
  99. return "checkbox" == a.type;
  100. },
  101. file : function(a) {
  102. return "file" == a.type;
  103. },
  104. password : function(a) {
  105. return "password" == a.type;
  106. },
  107. submit : function(a) {
  108. return "submit" == a.type;
  109. },
  110. image : function(a) {
  111. return "image" == a.type;
  112. },
  113. reset : function(a) {
  114. return "reset" == a.type;
  115. },
  116. button : function(a) {
  117. return "button" == a.type || jQuery.nodeName(a, "button");
  118. },
  119. input : function(a) {
  120. return /input|select|textarea|button/i.test(a.nodeName);
  121. },
  122. // :has()
  123. has : function(a, i, m) {
  124. return jQuery.find(m[3], a).length;
  125. },
  126. // :header
  127. header : function(a) {
  128. return /h\d/i.test(a.nodeName);
  129. },
  130. // :animated
  131. animated : function(a) {
  132. return jQuery.grep(jQuery.timers, function(fn) {
  133. return a == fn.elem;
  134. }).length;
  135. }
  136. }
  137. },
  138. // The regular expressions that power the parsing engine
  139. parse : [
  140. // Match: [@value='test'], [@foo]
  141. /^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
  142. // Match: :contains('foo')
  143. /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
  144. // Match: :even, :last-child, #id, .class
  145. new RegExp("^([:.#]*)(" + chars + "+)")],
  146. multiFilter : function(expr, elems, not) {
  147. var old, cur = [];
  148. while (expr && expr != old) {// 存在且改变
  149. old = expr;
  150. var f = jQuery.filter(expr, elems, not);
  151. expr = f.t.replace(/^\s*,\s*/, "");
  152. cur = not ? elems = f.r : jQuery.merge(cur, f.r);
  153. }
  154. return cur;
  155. },
  156. find : function(t, context) {
  157. if (typeof t != "string")
  158. return [t];// 快速处理非字符表达式
  159. if (context && context.nodeType != 1 && context.nodeType != 9)
  160. return [];// 确保context是DOM元素或document
  161. context = context || document;// 缺省的context
  162. // 初始化,ret:result, done:已经完成,last:上一次的t,nodeName:节点名,如p
  163. var ret = [context], done = [], last, nodeName;
  164. while (t && last != t) {// t存在,且变化
  165. var r = []; // ret的tempValue
  166. last = t; // last:上一次的t
  167. t = jQuery.trim(t);// 去首尾空格
  168. var foundToken = false, re = quickChild, // 以>开头的regexp
  169. m = re.exec(t);
  170. if (m) {// 首先判断是不是以>开头
  171. nodeName = m[1].toUpperCase();
  172. // 找到上下文的那些满足regexp中nodeName的所有子节点。
  173. for (var i = 0;ret[i]; i++)
  174. for (var c = ret[i].firstChild;c; c = c.nextSibling)
  175. if (c.nodeType == 1
  176. && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName))
  177. r.push(c);
  178. ret = r; // 现在找到的所有元素都是上下文(context)
  179. t = t.replace(re, "");// remove已经处理过的部分
  180. if (t.indexOf(" ") == 0)// 说明是 "E F"这样的形式
  181. continue;// 循环就能找到所有
  182. foundToken = true;// 找到标识
  183. } else {// 第二判断是不是以+~开头
  184. re = /^([>+~])\s*(\w*)/i;
  185. if ((m = re.exec(t)) != null) {// 以+~开头的
  186. r = [];
  187. var merge = {};
  188. nodeName = m[2].toUpperCase();// 节点名
  189. m = m[1];// 符号,如+,~
  190. // 如果参数t匹配" "或>(子元素),由context的第一个子元素开始遍历,
  191. // 如果参数t匹配~或+(后续元素),则从context的下一个元素开始遍历
  192. for (var j = 0, rl = ret.length;j < rl; j++) {// 已经找到的节点(context)遍历
  193. // 把~和+的操作统一在一起进行处理
  194. var n = (m == "~" || m == "+"
  195. ? ret[j].nextSibling
  196. : ret[j].firstChild);
  197. for (;n; n = n.nextSibling)
  198. if (n.nodeType == 1) {// 保证节点是元素类型
  199. var id = jQuery.data(n);// 为n元素生成全局唯一的id
  200. if (m == "~" && merge[id])// 保证ret中元素不重复
  201. break;// nextSibling会循环到第一个节点?
  202. if (!nodeName
  203. || n.nodeName.toUpperCase() == nodeName) {
  204. if (m == "~")// 找到元素保存起来
  205. merge[id] = true;
  206. r.push(n);
  207. }
  208. if (m == "+")// 直接后续兄弟节点,只进行一次操作。
  209. break;
  210. }
  211. }
  212. ret = r;// 找到元素的后续操作
  213. t = jQuery.trim(t.replace(re, ""));
  214. foundToken = true;
  215. }
  216. }
  217. if (t && !foundToken) {// 不是以>~+开头的
  218. if (!t.indexOf(",")) {// ,分隔出现在第一个位置上
  219. if (context == ret[0])
  220. ret.shift();// 把初始化给定的context清除出ret
  221. done = jQuery.merge(done, ret);// ret的其它元素放入done
  222. r = ret = [context];// 重新初始化
  223. // Touch up the selector string
  224. t = " " + t.substr(1, t.length);
  225. } else {// 采用,分隔的多表达式
  226. /*
  227. * qId:^((?:[\w*_-]|\.)+)(#)((?:[\w*_-]|\.)+)
  228. * qclass:^([#.]?)((?:[\\w*_-]|\.)*)
  229. */
  230. var re2 = quickID;// 如(.)nodeName#idName
  231. var m = re2.exec(t);// 找到第一个相配的
  232. if (m) {
  233. m = [0, m[2], m[3], m[1]];// m=[0,#,idName,nodeName]
  234. } else {
  235. re2 = quickClass;// #nodeName,.className
  236. m = re2.exec(t);// m=[all,#,idName]
  237. }
  238. m[2] = m[2].replace(/\\/g, "");// 去除转义字符
  239. var elem = ret[ret.length - 1];// 结果数组最后一个
  240. // 根据Id直接找到元素并确保其的确存在
  241. if (m[1] == "#" && elem && elem.getElementById
  242. && !jQuery.isXMLDoc(elem)) {
  243. var oid = elem.getElementById(m[2]);
  244. // 回测元素的ID的确存在,在IE中会取name属性的值,同时在form 元素中
  245. // 会选中在form中元素的name属性为id的元素。
  246. if ((jQuery.browser.msie || jQuery.browser.opera) && oid
  247. && typeof oid.id == "string" && oid.id != m[2])
  248. oid = jQuery('[@>0];
  249. // 回测元素的node Name是否相同,如div#foo,可以提交效率
  250. ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3]))
  251. ? [oid]
  252. : [];
  253. } else {// 找到结果集合中每个元素所有的后代元素组成集合,进行排查
  254. for (var i = 0;ret[i]; i++) {
  255. var tag = (m[1] == "#" && m[3] ? m[3] : (m[1] != ""
  256. || m[0] == "" ? "*" : m[2]));// 分情况取tagName
  257. if (tag == "*"
  258. && ret[i].nodeName.toLowerCase() == "object")
  259. tag = "param";// Handle IE7 being really dumb
  260. // about <object>s
  261. r = jQuery.merge(r, ret[i].getElementsByTagName(tag));
  262. }
  263. if (m[1] == ".") // 根据class在找到结果集合中过滤
  264. r = jQuery.classFilter(r, m[2]);
  265. if (m[1] == "#") {// 对元素的id过滤,找到regexp给定id的元素
  266. var tmp = [];
  267. for (var i = 0;r[i]; i++)
  268. if (r[i].getAttribute("id") == m[2]) {
  269. tmp = [r[i]];
  270. break;
  271. }
  272. r = tmp;
  273. }
  274. ret = r;
  275. }
  276. t = t.replace(re2, "");
  277. }
  278. }
  279. if (t) {// 根据余下的selector,对找到的r集合中的元素进行过滤
  280. var val = jQuery.filter(t, r);
  281. ret = r = val.r;
  282. t = jQuery.trim(val.t);// 去首尾空格
  283. }
  284. }
  285. if (t)// selector出现,返回[]。
  286. ret = [];
  287. if (ret && context == ret[0])
  288. ret.shift();// 去掉根上下文
  289. done = jQuery.merge(done, ret);// 合并
  290. return done;
  291. },
  292. // 找到r中element中的className中含有m 或不含有的所有的元素
  293. classFilter : function(r, m, not) {
  294. m = " " + m + " ";
  295. var tmp = [];
  296. for (var i = 0;r[i]; i++) {
  297. var pass = (" " + r[i].className + " ").indexOf(m) >= 0;
  298. if (!not && pass || not && !pass)
  299. tmp.push(r[i]);
  300. }
  301. return tmp;
  302. },
  303. filter : function(t, r, not) {
  304. var last;
  305. while (t && t != last) {// t存在,且改变
  306. last = t;
  307. // Match: [@value='test'], [@foo]
  308. // 1、^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
  309. // Match: :contains('foo')
  310. // 2、^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
  311. // Match: :even, :last-child, #id, .class
  312. // 3、new RegExp("^([:.#]*)(" + chars + "+)")],
  313. // isSimple = /^.[^:#\[\.]*$/
  314. var p = jQuery.parse, m;
  315. for (var i = 0;p[i]; i++) {// 找到与jQuery.parse中regexp相配的
  316. m = p[i].exec(t);
  317. if (m) {
  318. t = t.substring(m[0].length);
  319. m[2] = m[2].replace(/\\/g, "");// 有可能会有没有转换的\去掉
  320. break;
  321. }
  322. }
  323. if (!m)// 与上面三种的regexp都不相配
  324. break;
  325. // :not(.class) 处理不包含.class的其它的元素
  326. if (m[1] == ":" && m[2] == "not")
  327. // 性能上优化 m[3]是.class经常出现
  328. r = isSimple.test(m[3])
  329. ? jQuery.filter(m[3], r, true).r
  330. : jQuery(r).not(m[3]);
  331. else if (m[1] == ".")// 性能上优化考虑
  332. r = jQuery.classFilter(r, m[2], not);
  333. else if (m[1] == "[") {// [@value='test']形式的属性选择
  334. var tmp = [], type = m[3];// 符号,如=
  335. for (var i = 0, rl = r.length;i < rl; i++) {
  336. var a = r[i], z = a[jQuery.props[m[2]] || m[2]];// 元素的属性值
  337. if (z == null || /style|href|src|selected/.test(m[2]))
  338. z = jQuery.attr(a, m[2]) || '';// 几个特殊的处理
  339. // [foo],[foo=aa][foo!=aa][foo^=aa][foo$=aa][foo~=aa]
  340. if ((type == "" && !!z || type == "=" && z == m[5]
  341. || type == "!=" && z != m[5] || type == "^=" && z
  342. && !z.indexOf(m[5]) || type == "$="
  343. && z.substr(z.length - m[5].length) == m[5] || (type == "*=" || type == "~=")
  344. && z.indexOf(m[5]) >= 0)
  345. ^ not)
  346. tmp.push(a);
  347. }
  348. r = tmp;
  349. } else if (m[1] == ":" && m[2] == "nth-child") {// 性能考量
  350. var merge = {}, tmp = [],
  351. // parse equations like 'even', 'odd', '5', '2n', '3n+2',
  352. // '4n-1', '-n+6'
  353. test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3] == "even" && "2n"
  354. || m[3] == "odd" && "2n+1" || !/\D/.test(m[3]) && "0n+"
  355. + m[3] || m[3]),
  356. // 计算器(first)n+(last),first=(-?)(\d*),last=((?:\+|-)?\d*)
  357. first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
  358. for (var i = 0, rl = r.length;i < rl; i++) {
  359. var node = r[i], parentNode = node.parentNode, id = jQuery
  360. .data(parentNode);
  361. if (!merge[id]) {// 为元素的每个子节点标上顺序号,作了不重复标识
  362. var c = 1;
  363. for (var n = parentNode.firstChild;n; n = n.nextSibling)
  364. if (n.nodeType == 1)
  365. n.nodeIndex = c++;
  366. merge[id] = true;
  367. }
  368. var add = false;
  369. if (first == 0) {// 0不能作除数
  370. if (node.nodeIndex == last)
  371. add = true;
  372. }
  373. // 处理3n+2这种形式同时表达式要大于0
  374. else if ((node.nodeIndex - last) % first == 0
  375. && (node.nodeIndex - last) / first >= 0)
  376. add = true;
  377. if (add ^ not)
  378. tmp.push(node);
  379. }
  380. r = tmp;
  381. } else {// 根据m[1]m[2]在Query.expr找到对应的处理函数
  382. var fn = jQuery.expr[m[1]];
  383. if (typeof fn == "object")
  384. fn = fn[m[2]];
  385. if (typeof fn == "string")
  386. fn = eval("false||function(a,i){return " + fn + ";}");
  387. // 执行处理函数过滤r
  388. r = jQuery.grep(r, function(elem, i) {
  389. return fn(elem, i, m, r);
  390. }, not);
  391. }
  392. }
  393. // Return an array of filtered elements (r)
  394. // and the modified expression string (t)
  395. return {
  396. r : r,
  397. t : t
  398. };
  399. },
  400. // dir:nextSibling elem:element
  401. dir : function(elem, dir) {
  402. var matched = [], cur = elem[dir];
  403. while (cur && cur != document) {
  404. if (cur.nodeType == 1)
  405. matched.push(cur);
  406. cur = cur[dir];
  407. }
  408. return matched;
  409. },
  410. // dir:nextSibling result:deep cur:current. elem :no use
  411. nth : function(cur, result, dir, elem) {
  412. result = result || 1;
  413. var num = 0;
  414. for (;cur; cur = cur[dir])
  415. if (cur.nodeType == 1 && ++num == result)
  416. break;
  417. return cur;
  418. },
  419. // 排除elem的n的后续兄弟元素节点。
  420. sibling : function(n, elem) {
  421. var r = [];
  422. for (;n; n = n.nextSibling) {
  423. if (n.nodeType == 1 && n != elem)
  424. r.push(n);
  425. }
  426. return r;
  427. }
  428. });