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