宝哥软件园

JavaScript实现DOM对象选择器

编辑:宝哥软件园 来源:互联网 时间:2021-09-07

目的:

根据传入的选择器类型选择第一个匹配的DOM对象。 DOM对象可以通过id获取,如$(' # adom '); DOM对象可以通过tagName获取,如$(' a '); DOM对象可以通过样式名获取,如$('。class a '); DOM对象可以通过属性匹配得到,如$('[data-log]'),$('[data-time=2015]'); DOM对象,如$('#adom。classa '),可以通过级联和组合获得;

思考:

有必要区分复合选择和单一选择。如果用单项选择,应该用自己的方法获得,如果用复合选择,应该筛选。

所以第一步要区分是单品还是组合。

实现方法是将传递给选择器的字符串转换为数组。如果数组的长度大于1,则它是一个复合选择。如果没有,判断是哪一个选择器。

If(微调(选择器)。拆分(“”)。length1) {//trim()方法用于删除字符串//复合选择器代码}开头和结尾的空白//确定是哪个单一选择器。第二步,确定是哪一个选择器,然后过滤返回第一个元素。

(1)判断,有两种方式:

方法1:使用正则表达式。

if(/#(?[ w u00c 0- ufff -]| 。))/.测试(选择器){//id选择器} if (/(?[ w u00c 0- ufff -]| 。))/.test(selector)){//标记选择器}if(/。(?[ w u00c 0- ufff -]| 。))/.test(selector)){//class selector } if(/[[a-za-z0-9 _- s]]$/。

var类型=trim(选择器)。charAt(0);开关(类型){case . ' ': //类选择器大小写' #': //id选择器大小写'[': //属性选择器default ://标记选择器} 根据选择器进行筛选。

直接对id和标签使用DOM方法。与document.getelementsby类的类名存在兼容性问题,因此您需要为IE定义一个方法。属性选择器需要遍历所有的DOM节点对象,选择符合要求的。

//ID选择器return document . getelementbyid(selector . slice(1,selector . length));//标记选择器return document . getelementsbytagname(选择器)[0];//类选择器if(document . getelementsbyclassname){ returndocument . getelementsbyclassname(selector . slice(1,selector . length))[0];}else{ var nodes=document.all?document . all : document . getelementsbytagname(' * ');for(var I=0;索引节点长度;I){ var class=nodes[I]. class name . split(/ s/);if(class . indexof(selector . slice(1))!=-1){ //indexOf不兼容,需要在原型上扩展返回节点[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=选择器[0];var值=选择器[1];if(value){ for(var I=0;i eles.lengthi ) { if(eles[i]。getAttribute(att)=value){ return eles[I];} } } else { for(var I=0;i eles.lengthi ) { if(eles[i]。getAttribute(att)){ return eles[I];}}}}第三步是实现复杂的选择器。

想法一:

最终筛选出来的DOM对象必须是满足最后一个选择器的DOM对象集之一,所以可以先选择这些对象,然后逐个检查它们的祖先元素,看它们是否符合上一个选择器,如果不符合就删除它们。迭代到最外面的选择器,剩下的DOM对象集合中的第一个DOM对象就是我们要找的DOM对象。

然后,如果有n个选择器,则需要n-1轮筛选。

这里有两件事要做:检查元素的祖先元素是否是选择器对象集之一。检查对象集中的每个元素,删除不合格的DOM对象。

定义两个函数来完成这两件事:

//递归检查ele的祖先对象是否符合选择器函数isParent(ele,str){ if(!IsArray(str)) {//如果不是数组str=to array(str);//转换为数组} if (ele。parentnode) {if (str。(元素)指数。parent node)-1){返回true} else { return isParent(ele . parent node,str);} } else { return false} }//从eles中删除其祖先对象不符合选择器函数filter ELES(ELES,STR) {If(!isArray(eles)){ eles=to array(eles);} for (var i=0,len=eles.length伊琳;i ) { if(!isParent(eles[i],str)) { eles.splice(i,1);I=I-1;} }返回eles}这个实现会有一个BUG,就是当HTML是这样的时候,会过滤掉“第一个”,但并不是我们预期的那样。

虽然在实践中很少为父元素和子元素定义相同的类名,但我们不能忽视这个BUG的存在。

这个实现的性能也很差,因为当他检查对象集中对象的祖先元素是否符合选择器时,他首先检查他的父元素,然后检查他的父元素的父元素,直到没有父元素。然后他还需要检查它是否匹配下一个选择器,所以他再次遍历他的父元素。有地方可以反复参观。

想法1的所有代码:

//需要一个可以选择所有元素的方法函数getElements(选择器){ //类选择器,返回全部项if(/ .((?[ w u00c 0- ufff -]| .) )/.测试(选择器)){ if(文档。getelementsbyclassname){返回文档。getelementsbyclassname(选择器。切片(1,选择器。长度));} var nodes=document.all?文件。所有:文件。getelementsbytagname(' * ');var arr=[];//用来保存符合的class name for(var I=0;索引节点长度;i ){ if(hasClass(nodes[i],selector.slice(1,selector。长度)){ arr。推送(节点[I]);} }返回arr} //ID选择器if(/#(?[ w u00c 0- ufff -]| .) )/.测试(选择器)){返回文档。getelementbyid(选择器。切片(1,选择器。长度));}//标记选择器if(/^((?[ w u00c 0- ufff -]| .) )/.测试(选择器)){返回文档。getelementsbytagname(选择器);} //属性选择器if(/^[[A-Za-z0-9_-S] ]$/.测试(选择器){选择器=选择器。切片(1,选择器。长度-1);var eles=文档。getelementsbytagname(' * ');选择器=选择器。split('=');var att=选择器[0];定义变量值=选择器[1];var arr=[];if(value){ for(var I=0;I eles . lentigi){ if(eles[I]).getAttribute(att)==value){ arr。推(eles[I]);} } } else { for(var I=0;I eles . lentigi){ if(eles[I]).getAttribute(att)){ arr。推(eles[I]);} } }返回arr}}//检查ele的祖先对象是否符合选择器函数isParent(ele,str){ if(!isArray(str)){ str=to array(str);} if(ele。父节点){ if(str。指数。父节点)-1){ 0返回true} else { return isParent(ele。父节点,字符串);} } else { return false}}//从扩展线性支出模型中删掉祖先对象不符合选择器的对象函数FlitErles(eles,str){ if(!isArray(eles)){ eles=to array(eles);} for(var I=0;I eles . lentigi){ if(!isParent(eles[i],str)) { eles.splice(i,1);I=I-1;} }返回eles}//DOM元素选择器函数$(选择器){ if(!选择器的类型===' string '){ return false;} //复合选择器如果(微调(选择器)。拆分("")。长度1){ var all=trim(选择器)。拆分("");var eles=GetElements(all[all。length-1]);对于(var I=2;我都。长度都是2。长度-I=0;I){ eles=Fleereles(eles,GetElements(all[all。长度-I]);}返回eles[0];} //ID选择器if(/#(?[ w u00c 0- ufff -]| .) )/.测试(选择器)){返回文档。getelementbyid(选择器。切片(1,选择器。长度));}//标记选择器,只返回第一个if(/^((?[ w u00c 0- ufff -]| .) )/.测试(选择器)){返回文档。getelementsbytagname(选择器)[0];} //类选择器if(/ .((?[ w u00c 0- ufff -]| .) )/.测试(选择器)){ if(文档。getelementsbyclassname){返回文档。getelementsbyclassname(选择器。切片(1,选择器。length))[0];} var nodes=document.all?文件。所有:文件。getelementsbytagname(' * ');for(var I=0;索引节点长度;i ){ if(hasClass(nodes[i],selector.slice(1,selector。长度))){返回节点[I];} } } //属性选择器if(/^[[A-Za-z0-9_-S] ]$/.测试(选择器){选择器=选择器。切片(1,选择器。长度-1);var eles=文档。getelementsbytagname(' * ');选择器=选择器。split('=');var att=选择器[0];定义变量值=选择器[1];if(value){ for(var I=0;I eles . lentigi){ if(eles[I]).getAttribute(att)=value){ return eles[I];} } } else { for(var I=0;I eles . lentigi){ if(eles[I]).getAttribute(att)){ return eles[I];} } } }}•思路二:

从最外层向里面筛选。

先从文件选出符合最外层选择器的对象集,目标对象一定是这个对象集的一个对象的子孙元素。

所以,遍历这个对象集中的每个元素,从中选出符合第二个选择器的对象集,然后再遍历新的对象集。

直到筛选完最后一个选择器,剩下的对象集中的第一个就是目标对象。

这个方法不需要区分符合选择器和单项选择器,也不需要重新定义获得所有元素的方法。

函数$(选择器){ var all=选择器。拆分(/ s/);var result=[],rooot=[document];for(var I=0;一、全部长度;I){ var type=all[I][0];开关(类型){ //ID大小写' #' :为(var j=0;j rooot . lengj){ var ele=rooot[j].getElementById(全部[i]).切片(1));if(ele){结果。push(ele);} } break//类大小写".":为(var j=0;j rooot.lengthj){ if(文档。getelementsbyclassname){ var eles=rooot[j].getElementsByClassName(全部[i]).切片(1));if(eles){ result=result。concat(数组。原型。切片。call(eles));} }else{ var arr=rooot[j].getElementsByTagName(' * ');for(var I=0;一、长度;i ) { if (hasClass(arr[i],CLaSS name)){ result。推动;} } } } break//属性case '[': var att=all[i].切片(1,全部[i].长度-1)。split('=');var key=att[0],value=att[1];for(var j=0;j rooot . lengj){ var eles=rooot[j].getElementsByTagName(' * ');for(var I=0;I eles . lengthi){ if(value){ for(var I=0;I eles . lentigi){ if(eles[I]).getAttribute(key)=value){ result。推(eles[I]);} } } else { for(var I=0;I eles . lentigi){ if(eles[I]).getAttribute(key)){结果。推(eles[I]);} } } } } break//的标记默认值:(var j=0;j rooot . lengj){ eles=rooot[j].getElementsByTagName(全部[I]);if(eles){ result=result。concat(数组。原型。切片。call(eles));} } }//switch rooot=result;结果=[];}//用于返回rooot[0];}用到的公共方法:

//IE9-不支持数组的indexOf()if(!数组。原型。indexof){ array。原型。indexof=function(value){ for(var I=0,len=this.length伊琳;i ) {如果(这个[我]==值){返回I;} } return-1;};}//检查ele是否有classNamefunction hasClass(ele,CLaSS name){ if(ele。CLaSS name){ var CLaSS=ele。CLaSS名称。拆分(/ s/);//这里必须要切成数组之后再判断if(class。indexof(类名)!=-1){返回真实} }返回false}//判断到达)是否为一个数组,返回一个弯曲件值函数isArray(arr){返回数组。isArray(arr)| |对象。原型。tostring。调用(arr)='[对象数组]';}//对字符串头尾进行空格字符的去除、包括全角半角空格、选项卡等,返回一个字符串函数trim(str){ return str.replace(/^[sufeffxa0]|[ s ufeff xa0]$/g ')}//把一个类数组转换成数组函数到数组(obj){ if(obj。nodetype==1){ return[obj];} var arr=[];for(var I=0;长度;我){ arr。推动;}返回arr}参考:

https://github。com/Baidu-ife/ife/blob/master/2015 _ spring/task 0002/review/demo/js/util _ demo。js https://github。com/stark Wang/ife/blob/master/task 0002/work/stark Wang/js/util。射流研究…

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

更多资讯
游戏推荐
更多+