is() 根据选择器、元素或 jQuery 对象来检测匹配元素集合,如果这些元素中至少有一个元素匹配给定的参数,则返回 true。
它产用是Sizzle技术。
那我们如何使用querySelector模仿呢?
由于querySelector只能查找dom以下的DOM,所以我考虑到将采用dom的父dom来使用querySelector
由于querySelector无法查找精确子DOM(它将会查找其所有的子孙DOM),而且,要查找的dom可能会有很多兄弟
所以要重新定位到要查找的dom就有困难了,这时,我又想到id,由于在HTML中我们会规范,界面中需要唯一的ID
有了以上的条件,JQuery.is的模拟就有可能了,直接上代码
第一种方案:
var tagTokenRe = /^(#)?([\w\\-\\*]+)/;
function is(dom, selector) {
var tokenMatch = selector.match(tagTokenRe);
if (tokenMatch) {
if (tokenMatch[1] == "#") {
if (dom.id != tokenMatch[2]) {
return false;
}
} else {
if (dom.tagName.toLowerCase() != tokenMatch[2].toLowerCase()) {
return false;
}
}
selector = selector.replace(tokenMatch[0], "");
if (!selector) return true;
}
var pdom;
if (dom && (pdom = dom['parentNode'])) {
var id = dom.id;
if (!id) {
dom.id = id = "_J" + ++$.guid;
}
selector = '#' + id + selector;
var ret = pdom.querySelector(selector);
return ret !== null;
}
return false;
}
以上缺点是要保障子dom的兄弟不能有相同的ID
为了保障,可以将
var id = dom.id;
if (!id) {
dom.id = id = "_J" + ++$.guid;
}
selector = '#' + id + selector;
var ret = pdom.querySelector(selector);
改为第二种方案
var id = dom.id;
dom.id = '_JIS_'
selector = '#' + dom.id + selector;
var ret = pdom.querySelector(selector);
dom.id = id?id:null;
这样就可以保证ID的唯一性,但会降低性能。
第三种方案,采用存JS实现的如下:
var modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
tagTokenRe = /^(#)?([\w\\-\\*]+)/,
tplRe = /\{(\d+)\}/g,
cache = {};
var matchers = [
{re: /^\.([\w-]+)/, select: 'n = byClassName(n, " {1} ");if(n.length==0) return false;\n'},
{re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/, select: 'n = byPseudo(n, "{1}", "{2}");if(n.length==0)return false;\n'},
{re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/, select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");if(n.length==0) return false;\n'},
{re: /^#([\w-]+)/, select: 'n = byId(n, "{1}");if(n.length==0) return false;\n'}
];
var operators = {
"=": function (a, v) {
return a == v;
}, "!=": function (a, v) {
return a != v;
}, "^=": function (a, v) {
return a && a.substr(0, v.length) == v;
}, "$=": function (a, v) {
return a && a.substr(a.length - v.length) == v;
}, "*=": function (a, v) {
return a && a.indexOf(v) !== -1;
}, "%=": function (a, v) {
return (a % v) == 0;
}, "|=": function (a, v) {
return a && (a == v || a.substr(0, v.length + 1) == v + '-');
}, "~=": function (a, v) {
return a && (' ' + a + ' ').indexOf(' ' + v + ' ') != -1;
}
};
var pseudos = {
"contains": function (c, v) {
var r = [], ri = -1;
for (var i = 0, ci; ci = c[i]; i++) {
if ((ci.textContent || ci.innerText || '').indexOf(v) != -1) {
r[++ri] = ci;
}
}
return r;
},
"checked": function (c) {
var r = [], ri = -1;
for (var i = 0, ci; ci = c[i]; i++) {
if (ci.checked == true) {
r[++ri] = ci;
}
}
return r;
}
};
function compile(path) {
// setup fn preamble
var fn = ["var f = function(root){\nvar n = root || document;\n"],
lastPath,
matchersLn = matchers.length,
modeMatch;
// strip leading slashes
while (path.substr(0, 1) == "/") {
path = path.substr(1);
}
while (path && lastPath != path) {
lastPath = path;
var tokenMatch = path.match(tagTokenRe);
if (tokenMatch) {
if (tokenMatch[1] == "#") {
fn[fn.length] = 'n = byId(n, "' + tokenMatch[2] + '");if(n.length==0) return false;\n';
} else {
fn[fn.length] = 'n = byTag(n, "' + tokenMatch[2] + '");if(n.length==0) return false;\n';
}
path = path.replace(tokenMatch[0], "");
}
while (!(modeMatch = path.match(modeRe))) {
var matched = false;
for (var j = 0; j < matchersLn; j++) {
var t = matchers[j];
var m = path.match(t.re);
if (m) {
fn[fn.length] = t.select.replace(tplRe, function (x, i) {
return m[i];
});
path = path.replace(m[0], "");
matched = true;
break;
}
}
// prevent infinite loop on bad selector
if (!matched) {
throw 'Error parsing selector, parsing failed at "' + path + '"';
}
}
}
// close fn out
fn[fn.length] = "\nreturn n.length>0;\n}";
// eval fn and return it
eval(fn.join(""));
return f;
}
function byId(cs, id) {
if (cs.tagName || cs == document) {
cs = [cs];
}
if (!id) {
return cs;
}
var result = [], ri = -1;
for (var i = 0, ci; ci = cs[i]; i++) {
if (ci && ci.id == id) {
result[++ri] = ci;
return result;
}
}
return result;
}
function byTag(cs, tagName) {
if (cs.tagName || cs == document) {
cs = [cs];
}
if (!tagName) {
return cs;
}
var result = [];
tagName = tagName.toLowerCase();
for (var i = 0, ci; ci = cs[i]; i++) {
if (ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName) {
result.push(ci);
}
}
return result;
}
function byAttribute(cs, attr, value, op, custom) {
var result = [],
ri = -1,
fn = operators[op],
a,
innerHTML;
for (var i = 0, ci; ci = cs[i]; i++) {
// skip non-element nodes.
if (ci.nodeType != 1) {
continue;
}
innerHTML = ci.innerHTML;
// we only need to change the property names if we're dealing with html nodes, not XML
if (innerHTML !== null && innerHTML !== undefined) {
if (attr == "class" || attr == "className") {
a = ci.className;
} else if (attr == "for") {
a = ci.htmlFor;
} else if (attr == "href") {
a = ci.getAttribute("href", 2);
} else {
a = ci.getAttribute(attr);
}
} else {
a = ci.getAttribute(attr);
}
if ((fn && fn(a, value)) || (!fn && a)) {
result[++ri] = ci;
}
}
return result;
}
function byClassName(nodeSet, cls) {
if (!cls) {
return nodeSet;
}
var result = [], ri = -1;
for (var i = 0, ci; ci = nodeSet[i]; i++) {
if ((' ' + ci.className + ' ').indexOf(cls) != -1) {
result[++ri] = ci;
}
}
return result;
}
function byPseudo(cs, name, value) {
if (cs.tagName || cs == document) {
cs = [cs];
}
return pseudos[name](cs, value);
}
经过1000000的测试:
第一种方案需要9849ms,第二种方案需要13124ms,第三种方法需要9729ms。
本文介绍三种模仿jQuery.is()方法的实现方案,并对比了它们的性能。第一种方案利用querySelector结合ID唯一性;第二种方案通过临时修改ID确保唯一性;第三种方案采用纯JS实现,解析选择器并构建查询函数。
153

被折叠的 条评论
为什么被折叠?



