简单css选择器的实现
前段时间阅读过jQuery的一部分源码,其中对于选择器(selector)的实现部分,感觉看下来比较吃力,于是就根据平时封装和收集的一些API,特别是dom操作部分的,自己实现了一个简单的css选择器,支持#id、.className、@name、tagName.className、node节点五种格式获取页面的元素或元素集合。
废话不多说,贴上js代码:
// dom操作对象 var dom = { // 检测window isWindow: function(obj) { return obj && typeof obj === 'object' && 'setInterval' in obj; }, // 是否是节点 isNode: function(obj) { return !!(obj && obj.nodeType); }, // 是否是节点列表 isNodeList: function(obj) { // xxx类型调用toString()方法返回[object xxx] return !!(obj && (obj.toString() === '[object NodeList]' || obj.toString() === '[object HTMLCollection]')); }, // 根据id获取元素 getElemByID: function(id) { return typeof id === 'string' ? document.getElementById(id) : id; }, // 根据tagName获取元素集合 getElemsByTagName: function(tagName, root) { var elems = (root || document).getElementsByTagName(tagName); return elems !== null && elems.length ? elems : null; }, // 根据className获取元素(返回的是一个HTMLCollection) getElemsByClassName: function(className, root, tagName) { root = root || document; // 没有传入根节点,则默认为document tagName = tagName || '*'; // 没有传入标签,则默认获得所有标签 var i = 0, classElements = [], elements = root.getElementsByTagName(tagName), elementsLen = elements.length; pattern = new RegExp('(^|\\s)' + className + '(\\s|$)'); // className为要搜索的参数 // 遍历所有元素,如果匹配到传入的className,则把对应的元素添加到数组中 for (; i < elementsLen; i++) { if (pattern.test(elements[i].className)) { classElements.push(elements[i]); } } return classElements; // 返回匹配的元素集合 }, // 根据name获取元素集合 getElemsByName: function(name) { var i, elems = document.getElementsByName(name), // 原生方法(注意浏览器支持情况) elemsLen, arr = []; return elems !== null && elems.length ? elems : null; // 返回HTMLCollection }, // 判断样式类是否存在 hasClass: function(elem, className) { var reg = new RegExp("^|\\s" + className + "\\s|$"); return reg.test(elem.className) ? true : false; } }; /** * 简单css选择器 支持#id,.className,@formName,还有tagName.className,node节点五种格式 * @param {String || Object} * @param {Element} [root] 可选,从哪个根节点查找 * @return {object || HTMLCollection} 单个元素或元素集合 */ var $ = function(selector, root) { // 重用变量 var i, elems, elemsLen, matchAry = []; // 如果选择器selector为空,则终止执行。 if (selector === undefined) { return; // 如果选择器为节点或者为节点列表,则直接返回。 } else if (dom.isNode(selector) || dom.isNodeList(selector)) { return selector; // 如果选择器为window对象,则直接返回。 } else if (dom.isWindow(selector)) { return selector; } var selector = selector.toString(); // 匹配"#id"的情况 if (selector.indexOf('#') === 0) { return dom.getElemByID(selector.substring(1)); // 匹配".className"的情况 } else if (selector.indexOf('.') === 0) { elems = dom.getElemsByClassName(selector.substring(1), root); if (elems.constructor && elems.constructor.toString().indexOf('Array') > -1) { // 判断elems为数组对象 matchAry = elems; } else { for (i = 0, elemsLen = elems.length; i < elemsLen; i++) { matchAry.push(elems[i]); } } return matchAry; // 返回匹配的元素集合 // 匹配"@name"的情况 } else if (selector.indexOf('@') === 0) { elems = dom.getElemsByName(selector.substring(1)); for (i = 0, elemsLen = elems.length; i < elemsLen; i++) { matchAry.push(elems[i]); } return matchAry; // 返回匹配的元素集合 // 匹配"tagName.className"的情况 } else { if (selector.indexOf('.') > 0 && selector.indexOf('.') < selector.length) { // 根据tagName获取元素集合 elems = dom.getElemsByTagName(selector.substring(0, selector.indexOf('.')), root); var className = selector.substr(selector.indexOf('.') + 1); for (i = 0, elemsLen = elems.length; i < elemsLen; i++) { // 如果元素匹配到className,则把该元素添加到数组matchAry中 if (dom.hasClass(elems[i], className)) { matchAry.push(elems[i]); } } } else { // 否者如果没有查找到".",则调用getElemsByTagName方法 matchAry = dom.getElemsByTagName(selector, root); } return matchAry; // 返回匹配的元素集合 } };
demo实例截图:
firebug下测试结果截图:
结语:虽然该css选择器与jQuery的实现比起来是不堪一击的,尤其是在实现技巧、性能方面。但在平时一些小项目或效果的实现中,对于js获取页面元素的操作,感觉就够用了,而不用总是依赖框架去实现一些页面的交互效果。