标准浏览器的选择器封装
querySelector和querySelectorAll能获取以W3C标准规定的CSS选择器方式获取节点和节点集合(array-like)。
那么,一个类库的选择器模块假如不需要兼容IE67的话,实现就非常简单了。按照一般类库的使用方式,通常获取节点都是$(selector,context),
也就是在context里寻找selector,然后以数组的形式返回。利用querySelectorAll来封装这样的功能,是一件很简单的事情。
首先,最简单的,函数的内部实现转换一定是context.querySelectorAll(selector);再将其结果转换为数组,如下面实现:
var $ = function(selector,context){ var elems = context.querySelectorAll(selector); return makeArray(elems); } //简单地将array-like转换为真正的数组 function makeArray(source){ var target = []; for(var i = 0,len = source.length; i < len; i++){ target[i] = source[i]; } return target; }
上面的代码,只是假设context是一个节点,但context有可能是一个节点集合,如我要在所有div中寻找span,写法将会是$('span',document.getElementsByTagName('div')),但querySelectorAll不能用在elements下,所以,需要将其一个一个分解成单个节点再分别获取selector组合成数组。那就可以先将context转换成数组,再遍历获取。如下:
var $ = function(selector,context){ var elems = []; //通过nodeName判断context是否是节点 if(context.nodeName){ elems = context.querySelectorAll(selector); elmes = makeArray(elems); }else{ context = makeArray(context); for(var i = 0, len = context.length; i < len; i++){ var temp = context[i].querySelectorAll(selector); elems = elems.concat(makeArray(temp)); } } return elems; } //简单地将array-like转换为真正的数组 function makeArray(source){ var target = []; for(var i = 0,len = source.length; i < len; i++){ target[i] = source[i]; } return target; }
这样子,貌似差不多了,但,假设有嵌套的相同名称的结构,如下:
<div id="a"> <span>span1</span> <span>span2</span> <div id="a1"><span>span3</span></div> </div> <div id="b"><span>span4</span></div>
div#a里面还嵌套了<
div id="a1"
><
span
>span3</
span
></
div
>,按照上面的代码,这里面的span将会被重复获取,所以,需再加以区别。
可以通过判断context里面的包含关系,如div#a1是否包含在div#a里面,是的话则不在获取,如下代码:
var $ = function(selector,context){ var elems = []; if(context.nodeName){ elems = context.querySelectorAll(selector); elmes = makeArray(elems); }else{ context = makeArray(context); var prevElem = context[0], curElem; for(var i = 0, len = context.length; i < len; i++){ curElem = context[i]; if(!contains(prevElem,curElem)){ prevElem = curElem; var temp = curElem.querySelectorAll(selector); elems = elems.concat(makeArray(temp)); } } } return elems; } //简单地将array-like转换为真正的数组 function makeArray(source){ var target = []; for(var i = 0,len = source.length; i < len; i++){ target[i] = source[i]; } return target; } function contains( root, el ){ // 按照原则,先判断标准浏览器 if( root.compareDocumentPosition ){ return !!( root.compareDocumentPosition(el) & 16 ); }else if( root.contains ){ return root !== el && root.contains( el ); } return false; }
现在,就可以说完成了。
最后,我们整理下makeArray函数,让他可以更强大一点点,最终的完整代码如下:
var $ = function(selector,context){ var elems = []; if(context.nodeName){ elems = context.querySelectorAll(selector); elmes = makeArray(elems); }else{ context = makeArray(context); var prevElem = context[0], curElem; for(var i = 0, len = context.length; i < len; i++){ curElem = context[i]; if(!contains(prevElem,curElem)){ prevElem = curElem; elems = makeArray(curElem.querySelectorAll(selector),elems); } } } return elems; } //简单地将array-like转换为真正的数组 function makeArray(source,target){ target = target || []; for(var i = 0,len = source.length; i < len; i++){ target[target.length] = source[i]; } return target; } function contains( root, el ){ // 按照原则,先判断标准浏览器 if( root.compareDocumentPosition ){ return !!( root.compareDocumentPosition(el) & 16 ); }else if( root.contains ){ return root !== el && root.contains( el ); } return false; }
参考资料:https://github.com/chenmnkken/easyjs/blob/master/src/selector.js