标准浏览器的选择器封装

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

posted @ 2012-10-21 23:07  肥杜  阅读(1046)  评论(0编辑  收藏  举报