if(trim(selector).split(" ").length > 1){ //trim()方法用于去除字符串开头和结尾的空白//复合选择器代码}//判断是哪一种单项选择器第二步,判断是哪一种单项选择器,然后进行筛选返回第一个元素。
if(/#((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){//ID选择器}if(/^((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){//Tag选择器}if(/.((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){//class选择器}if(/^[[A-Za-z0-9_-S]+]$/.test(selector)){ //属性选择器}•方法二:检查传入选择器的第一个字符
var type=trim(selector).charAt(0);switch(type){ case "."://class选择器 case "#"://id选择器 case "["://属性选择器 default://tag选择器}②根据选择器进行筛选。
//ID选择器return document.getElementById(selector.slice(1,selector.length));//tag选择器return document.getElementsByTagName(selector)[0];//类选择器if(document.getElementsByClassName){ return document.getElementsByClassName(selector.slice(1,selector.length))[0];}else{ var nodes = document.all ? document.all : document.getElementsByTagName("*"); for(var i=0;i<nodes.length;i++){var classes=nodes[i].className.split(/s+/); if(classes.indexOf(selector.slice(1))!=-1){ //indexOf不兼容,需要在原型上扩展return nodes[i];break; } } } }//属性选择器if(/^[[A-Za-z0-9_-S]+]$/.test(selector)){ selector = selector.slice(1,selector.length-1); var eles = document.getElementsByTagName("*"); selector = selector.split("="); var att = selector[0]; var value = selector[1]; if (value) {for (var i = 0; i < eles.length; i++) { if(eles[i].getAttribute(att)==value){return eles[i]; } } }else{for (var i = 0; i < eles.length; i++) { if(eles[i].getAttribute(att)){return eles[i]; } } }}第三步,实现复杂选择器。
//递归检查ele的祖先对象是否符合选择器function isParent(ele,str){ if (!isArray(str)) {//如果不是数组str = toArray(str); //转换成数组 } if (ele.parentNode) {if (str.indexOf(ele.parentNode)>-1) { return true;}else{ return isParent(ele.parentNode,str); } }else{return false; }}//从eles中删掉祖先对象不符合选择器的对象function fliterEles(eles,str){ if(!isArray(eles)){ eles = toArray(eles); } for (var i = 0,len=eles.length;i<len; i++) {if (!isParent(eles[i],str)) { eles.splice(i,1); i = i - 1;} } return eles;}这个实现会有一个BUG,就是当HTML是下面这样的时候,他会筛选出“第一个”,然而它并不是我们期待的。
虽然实际应用中很少会这样给父元素和子元素定义相同的class名,但我们不能忽略这个BUG的存在。
这个实现的性能也是很差的,因为当他检查对象集合中的一个对象的祖先元素是否符合一个选择器时,他先检查他的父元素,不满足的话再检查他父元素的父元素,一直到没有父元素为止。然后他还需要检查是否符合下一个选择器,这样他又遍历了一遍他的父元素。这里有重复访问的地方。
思路一的所有代码:
//需要一个可以选择所有元素的方法function getElements(selector){ //类选择器,返回全部项 if(/.((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){if(document.getElementsByClassName){ return document.getElementsByClassName(selector.slice(1,selector.length));}var nodes = document.all ? document.all : document.getElementsByTagName("*");var arr=[]; //用来保存符合的className; for(var i=0;i<nodes.length;i++){ if(hasClass(nodes[i],selector.slice(1,selector.length))){arr.push(nodes[i]); }}return arr; } //ID选择器 if(/#((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){return document.getElementById(selector.slice(1,selector.length)); } //tag选择器 if(/^((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){return document.getElementsByTagName(selector); } //属性选择器 if(/^[[A-Za-z0-9_-S]+]$/.test(selector)){selector = selector.slice(1,selector.length-1);var eles = document.getElementsByTagName("*");selector = selector.split("=");var att = selector[0];var value = selector[1];var arr = []; if (value) { for (var i = 0; i < eles.length; i++) {if(eles[i].getAttribute(att)==value){ arr.push(eles[i]);}}}else{ for (var i = 0; i < eles.length; i++) {if(eles[i].getAttribute(att)){ arr.push(eles[i]);}}}return arr; }}//检查ele的祖先对象是否符合选择器function isParent(ele,str){ if (!isArray(str)) {str = toArray(str); } if (ele.parentNode) {if (str.indexOf(ele.parentNode)>-1) { return true;}else{ return isParent(ele.parentNode,str); } }else{return false; }}//从eles中删掉祖先对象不符合选择器的对象function fliterEles(eles,str){ if(!isArray(eles)){ eles = toArray(eles); } for (var i = 0; i < eles.length; i++) {if (!isParent(eles[i],str)) { eles.splice(i,1); i = i - 1;} } return eles;}//DOM元素选择器function $(selector){ if(!typeof selector === "string"){return false; } //复合选择器 if(trim(selector).split(" ").length > 1){var all = trim(selector).split(" ");var eles = getElements(all[all.length-1]);for(var i = 2 ; i < all.length+2 && all.length-i >=0; i++){ eles = fliterEles(eles,getElements(all[all.length-i]));}return eles[0]; } //ID选择器 if(/#((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){return document.getElementById(selector.slice(1,selector.length)); } //tag选择器,只返回第一个 if(/^((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){return document.getElementsByTagName(selector)[0]; } //类选择器 if(/.((?:[wu00c0-uFFFF-]|\.)+)/.test(selector)){if(document.getElementsByClassName){ return document.getElementsByClassName(selector.slice(1,selector.length))[0];}var nodes = document.all ? document.all : document.getElementsByTagName("*");for(var i=0;i<nodes.length;i++){ if(hasClass(nodes[i],selector.slice(1,selector.length))){return nodes[i]; }}} //属性选择器 if(/^[[A-Za-z0-9_-S]+]$/.test(selector)){selector = selector.slice(1,selector.length-1);var eles = document.getElementsByTagName("*");selector = selector.split("=");var att = selector[0];var value = selector[1];if (value) { for (var i = 0; i < eles.length; i++) {if(eles[i].getAttribute(att)==value){ return eles[i];}}}else{ for (var i = 0; i < eles.length; i++) {if(eles[i].getAttribute(att)){ return eles[i];}}} }}•思路二:
function $(selector){ var all=selector.split(/s+/); var result = [],rooot=[document]; for (var i = 0; i < all.length; i++) {var type=all[i][0];switch(type){//IDcase "#" : for (var j = 0; j < rooot.length; j++) {var ele=rooot[j].getElementById(all[i].slice(1));if (ele) { result.push(ele);} } break;//classcase ".": for (var j = 0; j < rooot.length; j++) {if (document.getElementsByClassName) { var eles=rooot[j].getElementsByClassName(all[i].slice(1)); if (eles) {result=result.concat(Array.prototype.slice.call(eles)); }}else{ var arr = rooot[j].getElementsByTagName("*"); for (var i = 0; i < arr.length; i++) {if (hasClass(arr[i], className)) { result.push(arr[i]);} }} } break;//属性case "[": var att = all[i].slice(1,all[i].length-1).split("="); var key = att[0],value=att[1]; for (var j = 0; j < rooot.length; j++) {var eles=rooot[j].getElementsByTagName("*");for (var i = 0; i < eles.length; i++) { if (value) {for (var i = 0; i < eles.length; i++) { if(eles[i].getAttribute(key)==value){result.push(eles[i]); }} }else{for (var i = 0; i < eles.length; i++) { if(eles[i].getAttribute(key)){result.push(eles[i]); }} }} } break;//tagdefault: for (var j = 0; j < rooot.length; j++) {eles=rooot[j].getElementsByTagName(all[i]);if (eles) { result=result.concat(Array.prototype.slice.call(eles));} }}//switchrooot=result;result=[];}//for return rooot[0];}用到的公共方法:
//IE9-不支持数组的indexOf()if (!Array.prototype.indexOf) { Array.prototype.indexOf=function(value){for (var i = 0,len=this.length;i<len; i++) { if(this[i]==value){return i; }}return -1; };}//检查ele是否有classNamefunction hasClass(ele,className){ if (ele&&ele.className) {var classes=ele.className.split(/s+/);//这里必须要切成数组之后再判断if(classes.indexOf(className)!=-1){ return true;}} return false;}// 判断arr是否为一个数组,返回一个bool值function isArray(arr){ return Array.isArray(arr)||Object.prototype.toString.call(arr) === "[object Array]";}// 对字符串头尾进行空格字符的去除、包括全角半角空格、Tab等,返回一个字符串function trim(str){ return str.replace(/^[suFEFFxA0]+|[suFEFFxA0]+$/g,"")}//把一个类数组转换成数组function toArray(obj){ if (obj.nodeType == 1 ) {return [obj]; } var arr = []; for( var i = 0 ; i < obj.length ; i++){arr.push(obj[i]); } return arr;}参考: