这一节将进行KISSY的选择器 selector.js分析.
【程序源码】
J1616.add('selector', function(J, undefined) {
var doc = document,
DOM = J.DOM,
SPACE = ' ',
ANY = '*',
REG_ID = /^#[\w-]+$/,
REG_QUERY = /^(?:#([\w-]+))?\s*([\w-]+|\*)?\.?([\w-]+)?$/;
function query(selector, context) {
var match, t, ret = [], id, tag, cls, i, len;
// #id
// tag
// .cls
// #id tag
// #id .cls
// tag.cls
// #id tag.cls
// #id.cls
if (J.isString(selector)) {
selector = J.trim(selector);
// 优先处理selector 为 #id 的情况
if (REG_ID.test(selector)) {
t = getElementById(selector.slice(1));
if (t) ret = [t]; // #id无效时 返回空数组
}
// selector为其他6种
else if ((match = REG_QUERY.exec(selector))) {
id = match[1];
tag = match[2];
cls = match[3];
// $('#id', context[如果前面含有ID选择器,则无需限定环境,直接ID就是最直接的: 否则限定])
if ((context = id ? getElementById(id) : tuneContext(context))) {
// #id .cls | #id tag.cls | .cls | tag.cls
if (cls) {
if (!id || selector.indexOf(SPACE) !== -1) { // 排除 #id.cls
ret = getElementsByClassName(cls, tag, context);
}
// #id.cls
else {
t = getElementById(id);
if (t && DOM.hasClass(t, cls)) {
ret = [t];
}
}
}
// #id tag | tag
else if (tag) {
ret = getElementsByTagName(context, tag);
}
}
}
// 分组选择器
else if (selector.indexOf(',') > -1) {
if (doc.querySelectorAll) {
ret = doc.querySelectorAll(selector);
} else {
var parts = selector.split(','), r = [];
for (i = 0, len = parts.length; i < len; ++i) {
r = r.concat(query(parts[i]), context);
}
ret = uniqueSort(r);
}
}
// 采用外部选择器
else if (J.ExternalSelector) {
return J.ExternalSelector(selector, context);
}
// 依旧不支持, 抛异常
else {
error(selector);
}
}
// 传入的Selector是Node
else if (selector && selector.nodeType) {
ret = [selector];
}
// 传入的Selector是NodeList或Array
else if (selector && (selector.item || J.isArray(selector))) {
ret = selector;
}
// 传入的 selector是其他值, 返回空数组
// 将NodeList转为普通数组
if (ret.item) {
ret = J.makeArray(ret);
}
return ret;
}
// 调整 context 为合理值
function tuneContext(context) {
if (context === undefined) {
context = doc;
}
else if (J.isString(context) && REG_ID.test(context)) {
context = getElementById(context.slice(1));
}
else if (context && context.nodeType !== 1 && context.nodeType !== 9) {
context = null;
}
return context;
}
// query #id
function getElementById(id) {
return doc.getElementById(id);
}
// query tag
function getElementsByTagName(el, tag) {
return el.getElementsByTagName(tag);
}
// 如果byTagName(el, '*') IE下会连注释节点一起筛选出来
// 下面用特性检测 将getElementsByTagName覆盖
(function() {
var div = doc.createElement('div');
div.appendChild(doc.createComment(''));
if (div.getElementsByTagName(ANY).length > 0) {
getElementsByTagName = function(el, tag) {
var ret = el.getElementsByTagName(tag);
if (tag === ANY) {
var t = [], i = 0, j = 0, node;
while ((node = ret[i++])) {
// filter
if (node.nodeType === 1) {
t[j++] = node
}
}
ret = t;
}
return ret;
};
}
})();
// query .cls
function getElementsByClassName(cls, tag, context) {
var els = context.getElementsByClassName(cls),
ret = els, i = 0, j = 0, len = els.length, el;
if (tag && tag !== ANY) {
ret = [];
tag = tag.toUpperCase();
for (; i < len; ++i) {
el = els[i];
if (el.tagName === tag) {
ret[j++] = el
}
}
}
return ret;
}
if (!doc.getElementsByClassName) {
// 降级使用 querySelectorAll
if (doc.querySelectorAll) {
getElementsByClassName = function(cls, tag, context) {
return context.querySelectorAll((tag ? tag : '') + '.' + cls);
}
}
// 降级到普通方法
else {
getElementsByClassName = function(cls, tag, context) {
var els = context.getElementsByTagName(tag || ANY),
ret = [], i = 0, j = 0, len = els.length, el, t;
cls = SPACE + cls + SPACE;
for (; i < len; ++i) {
el = els[i];
t = el.className;
if (t && (SPACE + t + SPACE).indexOf(cls) > -1) {
ret[j++] = el;
}
}
return ret;
}
}
}
// 对于分组选择器, 需要进行去重和排序
function uniqueSort(results) {
var hasDuplicate = false;
// 按照DOM位置排序
results.sort(function(a, b) {
var ret = a.sourceIndex - b.sourceIndex;
if (ret === 0) {
hasDuplicate = true;
}
return ret;
});
if (hasDuplicate) {
for (var i = 1; i < results.length; i++) {
if (results[i] === results[i - 1]) {
results.splice(i--, i);
}
}
}
return results;
}
// throw exception
function error(msg) {
J.error('Unsupport selector: ' + msg);
}
J.query = query;
J.get = function(selector, context) {
return query(selector, context)[0] || null;
};
J.mix(DOM, {
query: query,
get: J.get,
filter: function(selector, filter) {
var elems = query(selector), match, tag, cls, ret = [];
// 默认仅支持最简单的 tag.cls形式
if (J.isString(filter) && (match = REG_QUERY.exec(filter)) && !match[1]) {
tag = match[2];
cls = match[3];
filter = function(elem) {
return !((tag && elem.tagName !== tag.toUpperCase()) || (cls && !DOM.hasClass(elem, cls)));
}
}
if (J.isFunction(filter)) {
ret = J.filter(elems, filter);
}
// 其他复杂的filter, 采用外部选择器
else if (filter && J.ExternalSelector) {
ret = J.ExternalSelector._filter(selector, filter);
}
// selector为空 或者 不支持
else {
error(filter);
}
return ret;
},
test: function(selector, filter) {
var elems = query(selector);
return DOM.filter(elems, filter).length === elems.length;
}
});
});