jquery Selector 源码分析
- /**
- * author:prk
- * date:2008-08-04
- * comment:comment for selector of jQuery
- *
- */
- var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417
- ? "(?:[\\w*_-]|\\\\.)"
- : "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)", quickChild = new RegExp("^>\\s*("
- + chars + "+)"), quickID = new RegExp("^(" + chars + "+)(#)(" + chars
- + "+)"), // ^((?:[\\w*_-]|\\\\.))(#)((?:[\\w*_-]|\\\\.))
- quickClass = new RegExp("^([#.]?)(" + chars + "*)");// ^([#.]?)((?:[\\w*_-]|\\\\.)*)
- jQuery.extend( {
- expr : {
- "" : function(a, i, m) {
- return m[2] == "*" || jQuery.nodeName(a, m[2]);
- },
- "#" : function(a, i, m) {
- return a.getAttribute("id") == m[2];
- },
- ":" : {
- // Position Checks
- lt : function(a, i, m) {
- return i < m[3] - 0;
- },
- gt : function(a, i, m) {
- return i > m[3] - 0;
- },
- nth : function(a, i, m) {
- return m[3] - 0 == i;
- },
- eq : function(a, i, m) {
- return m[3] - 0 == i;
- },
- first : function(a, i) {
- return i == 0;
- },
- last : function(a, i, m, r) {
- return i == r.length - 1;
- },
- even : function(a, i) {
- return i % 2 == 0;
- },
- odd : function(a, i) {
- return i % 2;
- },
- // Child Checks
- "first-child" : function(a) {
- return a.parentNode.getElementsByTagName("*")[0] == a;
- },
- "last-child" : function(a) {
- return jQuery.nth(a.parentNode.lastChild, 1, "previousSibling") == a;
- },
- "only-child" : function(a) {
- return !jQuery.nth(a.parentNode.lastChild, 2, "previousSibling");
- },
- // Parent Checks
- parent : function(a) {
- return a.firstChild;
- },
- empty : function(a) {
- return !a.firstChild;
- },
- // Text Check
- contains : function(a, i, m) {
- return (a.textContent || a.innerText || jQuery(a).text() || "")
- .indexOf(m[3]) >= 0;
- },
- // Visibility
- visible : function(a) {
- return "hidden" != a.type && jQuery.css(a, "display") != "none"
- && jQuery.css(a, "visibility") != "hidden";
- },
- hidden : function(a) {
- return "hidden" == a.type || jQuery.css(a, "display") == "none"
- || jQuery.css(a, "visibility") == "hidden";
- },
- // Form attributes
- enabled : function(a) {
- return !a.disabled;
- },
- disabled : function(a) {
- return a.disabled;
- },
- checked : function(a) {
- return a.checked;
- },
- selected : function(a) {
- return a.selected || jQuery.attr(a, "selected");
- },
- // Form elements
- text : function(a) {
- return "text" == a.type;
- },
- radio : function(a) {
- return "radio" == a.type;
- },
- checkbox : function(a) {
- return "checkbox" == a.type;
- },
- file : function(a) {
- return "file" == a.type;
- },
- password : function(a) {
- return "password" == a.type;
- },
- submit : function(a) {
- return "submit" == a.type;
- },
- image : function(a) {
- return "image" == a.type;
- },
- reset : function(a) {
- return "reset" == a.type;
- },
- button : function(a) {
- return "button" == a.type || jQuery.nodeName(a, "button");
- },
- input : function(a) {
- return /input|select|textarea|button/i.test(a.nodeName);
- },
- // :has()
- has : function(a, i, m) {
- return jQuery.find(m[3], a).length;
- },
- // :header
- header : function(a) {
- return /h\d/i.test(a.nodeName);
- },
- // :animated
- animated : function(a) {
- return jQuery.grep(jQuery.timers, function(fn) {
- return a == fn.elem;
- }).length;
- }
- }
- },
- // The regular expressions that power the parsing engine
- parse : [
- // Match: [@value='test'], [@foo]
- /^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
- // Match: :contains('foo')
- /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
- // Match: :even, :last-child, #id, .class
- new RegExp("^([:.#]*)(" + chars + "+)")],
- multiFilter : function(expr, elems, not) {
- var old, cur = [];
- while (expr && expr != old) {// 存在且改变
- old = expr;
- var f = jQuery.filter(expr, elems, not);
- expr = f.t.replace(/^\s*,\s*/, "");
- cur = not ? elems = f.r : jQuery.merge(cur, f.r);
- }
- return cur;
- },
- find : function(t, context) {
- if (typeof t != "string")
- return [t];// 快速处理非字符表达式
- if (context && context.nodeType != 1 && context.nodeType != 9)
- return [];// 确保context是DOM元素或document
- context = context || document;// 缺省的context
- // 初始化,ret:result, done:已经完成,last:上一次的t,nodeName:节点名,如p
- var ret = [context], done = [], last, nodeName;
- while (t && last != t) {// t存在,且变化
- var r = []; // ret的tempValue
- last = t; // last:上一次的t
- t = jQuery.trim(t);// 去首尾空格
- var foundToken = false, re = quickChild, // 以>开头的regexp
- m = re.exec(t);
- if (m) {// 首先判断是不是以>开头
- nodeName = m[1].toUpperCase();
- // 找到上下文的那些满足regexp中nodeName的所有子节点。
- for (var i = 0;ret[i]; i++)
- for (var c = ret[i].firstChild;c; c = c.nextSibling)
- if (c.nodeType == 1
- && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName))
- r.push(c);
- ret = r; // 现在找到的所有元素都是上下文(context)
- t = t.replace(re, "");// remove已经处理过的部分
- if (t.indexOf(" ") == 0)// 说明是 "E F"这样的形式
- continue;// 循环就能找到所有
- foundToken = true;// 找到标识
- } else {// 第二判断是不是以+~开头
- re = /^([>+~])\s*(\w*)/i;
- if ((m = re.exec(t)) != null) {// 以+~开头的
- r = [];
- var merge = {};
- nodeName = m[2].toUpperCase();// 节点名
- m = m[1];// 符号,如+,~
- // 如果参数t匹配" "或>(子元素),由context的第一个子元素开始遍历,
- // 如果参数t匹配~或+(后续元素),则从context的下一个元素开始遍历
- for (var j = 0, rl = ret.length;j < rl; j++) {// 已经找到的节点(context)遍历
- // 把~和+的操作统一在一起进行处理
- var n = (m == "~" || m == "+"
- ? ret[j].nextSibling
- : ret[j].firstChild);
- for (;n; n = n.nextSibling)
- if (n.nodeType == 1) {// 保证节点是元素类型
- var id = jQuery.data(n);// 为n元素生成全局唯一的id
- if (m == "~" && merge[id])// 保证ret中元素不重复
- break;// nextSibling会循环到第一个节点?
- if (!nodeName
- || n.nodeName.toUpperCase() == nodeName) {
- if (m == "~")// 找到元素保存起来
- merge[id] = true;
- r.push(n);
- }
- if (m == "+")// 直接后续兄弟节点,只进行一次操作。
- break;
- }
- }
- ret = r;// 找到元素的后续操作
- t = jQuery.trim(t.replace(re, ""));
- foundToken = true;
- }
- }
- if (t && !foundToken) {// 不是以>~+开头的
- if (!t.indexOf(",")) {// ,分隔出现在第一个位置上
- if (context == ret[0])
- ret.shift();// 把初始化给定的context清除出ret
- done = jQuery.merge(done, ret);// ret的其它元素放入done
- r = ret = [context];// 重新初始化
- // Touch up the selector string
- t = " " + t.substr(1, t.length);
- } else {// 采用,分隔的多表达式
- /*
- * qId:^((?:[\w*_-]|\.)+)(#)((?:[\w*_-]|\.)+)
- * qclass:^([#.]?)((?:[\\w*_-]|\.)*)
- */
- var re2 = quickID;// 如(.)nodeName#idName
- var m = re2.exec(t);// 找到第一个相配的
- if (m) {
- m = [0, m[2], m[3], m[1]];// m=[0,#,idName,nodeName]
- } else {
- re2 = quickClass;// #nodeName,.className
- m = re2.exec(t);// m=[all,#,idName]
- }
- m[2] = m[2].replace(/\\/g, "");// 去除转义字符
- var elem = ret[ret.length - 1];// 结果数组最后一个
- // 根据Id直接找到元素并确保其的确存在
- if (m[1] == "#" && elem && elem.getElementById
- && !jQuery.isXMLDoc(elem)) {
- var oid = elem.getElementById(m[2]);
- // 回测元素的ID的确存在,在IE中会取name属性的值,同时在form 元素中
- // 会选中在form中元素的name属性为id的元素。
- if ((jQuery.browser.msie || jQuery.browser.opera) && oid
- && typeof oid.id == "string" && oid.id != m[2])
- oid = jQuery('[@id="' + m[2] + '"]', elem)[0];
- // 回测元素的node Name是否相同,如div#foo,可以提交效率
- ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3]))
- ? [oid]
- : [];
- } else {// 找到结果集合中每个元素所有的后代元素组成集合,进行排查
- for (var i = 0;ret[i]; i++) {
- var tag = (m[1] == "#" && m[3] ? m[3] : (m[1] != ""
- || m[0] == "" ? "*" : m[2]));// 分情况取tagName
- if (tag == "*"
- && ret[i].nodeName.toLowerCase() == "object")
- tag = "param";// Handle IE7 being really dumb
- // about <object>s
- r = jQuery.merge(r, ret[i].getElementsByTagName(tag));
- }
- if (m[1] == ".") // 根据class在找到结果集合中过滤
- r = jQuery.classFilter(r, m[2]);
- if (m[1] == "#") {// 对元素的id过滤,找到regexp给定id的元素
- var tmp = [];
- for (var i = 0;r[i]; i++)
- if (r[i].getAttribute("id") == m[2]) {
- tmp = [r[i]];
- break;
- }
- r = tmp;
- }
- ret = r;
- }
- t = t.replace(re2, "");
- }
- }
- if (t) {// 根据余下的selector,对找到的r集合中的元素进行过滤
- var val = jQuery.filter(t, r);
- ret = r = val.r;
- t = jQuery.trim(val.t);// 去首尾空格
- }
- }
- if (t)// selector出现,返回[]。
- ret = [];
- if (ret && context == ret[0])
- ret.shift();// 去掉根上下文
- done = jQuery.merge(done, ret);// 合并
- return done;
- },
- // 找到r中element中的className中含有m 或不含有的所有的元素
- classFilter : function(r, m, not) {
- m = " " + m + " ";
- var tmp = [];
- for (var i = 0;r[i]; i++) {
- var pass = (" " + r[i].className + " ").indexOf(m) >= 0;
- if (!not && pass || not && !pass)
- tmp.push(r[i]);
- }
- return tmp;
- },
- filter : function(t, r, not) {
- var last;
- while (t && t != last) {// t存在,且改变
- last = t;
- // Match: [@value='test'], [@foo]
- // 1、^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,
- // Match: :contains('foo')
- // 2、^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,
- // Match: :even, :last-child, #id, .class
- // 3、new RegExp("^([:.#]*)(" + chars + "+)")],
- // isSimple = /^.[^:#\[\.]*$/
- var p = jQuery.parse, m;
- for (var i = 0;p[i]; i++) {// 找到与jQuery.parse中regexp相配的
- m = p[i].exec(t);
- if (m) {
- t = t.substring(m[0].length);
- m[2] = m[2].replace(/\\/g, "");// 有可能会有没有转换的\去掉
- break;
- }
- }
- if (!m)// 与上面三种的regexp都不相配
- break;
- // :not(.class) 处理不包含.class的其它的元素
- if (m[1] == ":" && m[2] == "not")
- // 性能上优化 m[3]是.class经常出现
- r = isSimple.test(m[3])
- ? jQuery.filter(m[3], r, true).r
- : jQuery(r).not(m[3]);
- else if (m[1] == ".")// 性能上优化考虑
- r = jQuery.classFilter(r, m[2], not);
- else if (m[1] == "[") {// [@value='test']形式的属性选择
- var tmp = [], type = m[3];// 符号,如=
- for (var i = 0, rl = r.length;i < rl; i++) {
- var a = r[i], z = a[jQuery.props[m[2]] || m[2]];// 元素的属性值
- if (z == null || /style|href|src|selected/.test(m[2]))
- z = jQuery.attr(a, m[2]) || '';// 几个特殊的处理
- // [foo],[foo=aa][foo!=aa][foo^=aa][foo$=aa][foo~=aa]
- if ((type == "" && !!z || type == "=" && z == m[5]
- || type == "!=" && z != m[5] || type == "^=" && z
- && !z.indexOf(m[5]) || type == "$="
- && z.substr(z.length - m[5].length) == m[5] || (type == "*=" || type == "~=")
- && z.indexOf(m[5]) >= 0)
- ^ not)
- tmp.push(a);
- }
- r = tmp;
- } else if (m[1] == ":" && m[2] == "nth-child") {// 性能考量
- var merge = {}, tmp = [],
- // parse equations like 'even', 'odd', '5', '2n', '3n+2',
- // '4n-1', '-n+6'
- test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3] == "even" && "2n"
- || m[3] == "odd" && "2n+1" || !/\D/.test(m[3]) && "0n+"
- + m[3] || m[3]),
- // 计算器(first)n+(last),first=(-?)(\d*),last=((?:\+|-)?\d*)
- first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
- for (var i = 0, rl = r.length;i < rl; i++) {
- var node = r[i], parentNode = node.parentNode, id = jQuery
- .data(parentNode);
- if (!merge[id]) {// 为元素的每个子节点标上顺序号,作了不重复标识
- var c = 1;
- for (var n = parentNode.firstChild;n; n = n.nextSibling)
- if (n.nodeType == 1)
- n.nodeIndex = c++;
- merge[id] = true;
- }
- var add = false;
- if (first == 0) {// 0不能作除数
- if (node.nodeIndex == last)
- add = true;
- }
- // 处理3n+2这种形式同时表达式要大于0
- else if ((node.nodeIndex - last) % first == 0
- && (node.nodeIndex - last) / first >= 0)
- add = true;
- if (add ^ not)
- tmp.push(node);
- }
- r = tmp;
- } else {// 根据m[1]m[2]在Query.expr找到对应的处理函数
- var fn = jQuery.expr[m[1]];
- if (typeof fn == "object")
- fn = fn[m[2]];
- if (typeof fn == "string")
- fn = eval("false||function(a,i){return " + fn + ";}");
- // 执行处理函数过滤r
- r = jQuery.grep(r, function(elem, i) {
- return fn(elem, i, m, r);
- }, not);
- }
- }
- // Return an array of filtered elements (r)
- // and the modified expression string (t)
- return {
- r : r,
- t : t
- };
- },
- // dir:nextSibling elem:element
- dir : function(elem, dir) {
- var matched = [], cur = elem[dir];
- while (cur && cur != document) {
- if (cur.nodeType == 1)
- matched.push(cur);
- cur = cur[dir];
- }
- return matched;
- },
- // dir:nextSibling result:deep cur:current. elem :no use
- nth : function(cur, result, dir, elem) {
- result = result || 1;
- var num = 0;
- for (;cur; cur = cur[dir])
- if (cur.nodeType == 1 && ++num == result)
- break;
- return cur;
- },
- // 排除elem的n的后续兄弟元素节点。
- sibling : function(n, elem) {
- var r = [];
- for (;n; n = n.nextSibling) {
- if (n.nodeType == 1 && n != elem)
- r.push(n);
- }
- return r;
- }
- });
jquery Selector 源码分析相关推荐
- NIO - Selector源码分析
1. 背景 SelectableChannel对象的多路复用器. 可以通过调用Selector.open()方法创建Selector对象.Selector.open()方法会利用系统默认的Select ...
- jQuery Easyui 源码分析之combo组件
/** * jQuery EasyUI 1.3.1 * 该源码完全由压缩码翻译而来,并非网络上放出的源码,请勿索要.*/ (function($) {function setSize(target, ...
- jQuery方法源码解析--jQuery($)方法(一)
jQuery方法源码解析--jQuery($)方法 注: 1.本文分析的代码为jQuery.1.11.1版本,在官网上下载未压缩版即可 2.转载请注明出处 jQuery方法: 这个方法大家都不陌生,在 ...
- jQuery 2.0.3 源码分析core - 整体架构
拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery ...
- jQuery 源码分析第一篇之入口源码
目前阅读的是jQuery 1.11.3的源码,有参考nuysoft的资料.原来比较喜欢在自己的Evernote上做学习基类,并没有在网上写技术博客的习惯,现在开始学习JS的开源代码,想跟大家多交流,希 ...
- jQuery 2.0.3 源码分析 事件体系结构
那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...
- jQuery源码分析系列 : 整体架构
query这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧! ...
- 菜鸟读jQuery 2.0.3 源码分析系列(1)
原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...
- jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/unde
10.4 .bind() .one() 10.4.1 如何使用 .bind( eventType, [eventData], handler(eventObject) ) 在匹配的元素上绑 ...
最新文章
- 说说web缓存-强缓存、协商缓存
- c++ 可视化界面_这些算法可视化网站助你轻松学算法
- javaScript实现E-mail 验证
- 安卓学习笔记35:广播接收者
- oracle进入rman报错,Oracle 11g单实例RMAN恢复到Oracle 11g RAC
- This Android SDK requires Android Developer Toolkit version 23.0.0 or above
- 拓端tecdat|Python用Keras神经网络序列模型回归拟合预测、准确度检查和结果可视化
- mysql函数大全之数字函数
- 基于Python构建土地利用转移矩阵及完成Markov预测
- matlab中制作软件,如何用matlab制作一个小软件
- Tomcat 弱密码爆破 漏洞复现
- 测试地势高低的手机软件_能测量海拔高度的手机软件有哪些?
- Hadoop3.3.2+hbase2.4.10org.apache.hadoop.hbase.ipc.ServerNotRunningYetException: Server is not runn
- 解决awvs安装后访问不了登录页面
- matlab集群版,分布式Matlab计算集群建立方法与Demo
- VS2019 MFC ODBC 操作SQL SERVER 数据库
- 存储卡种类及其应用大盘点
- ES修改mapping中的字段名称
- 制造业信息化的长治久安之计--Agentflow解决方案简介
- Oracle Chp3 复杂查询 key point:数据分页;子查询;表连接;集合运算符
热门文章
- 大一java实训报告1500字_从800字小作文,到3000字小论文你用了多久? | 校媒FM
- 听说英飞凌内推技术岗位有大额奖金
- 第十六届全国大学生智能汽车竞赛总决赛 AI视觉组线上赛细则
- IO口多路查理复用:三个单片机IO口控制六个LED
- windows下cd无法切换到指定目录下
- 山东省2021年普通高考成绩录取去向查询,2018年山东高考录取去向查询时间及入口...
- 负数对2取余_不同语言的负数取余问题
- ubuntu下php服务器搭建_Ubuntu服务器下搭建php运行环境的方法
- linux lua ide,mac os上开发lua用什么ide
- delphi listview动态添加图片_网站图片如何优化适合收录