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。