您的位置:首页 > Web前端 > JQuery

jquery Selector 源码分析

2008-12-02 17:59 507 查看
/**

* 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;

}

});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: