js的学习中,我们总是借鉴或使用jQury,这时候学习前端的工程师们,就想自己封装一个,但是封装属于自己的框架,确实不太容易,所以,jQury使我们很好的一个范本,所以,我i门来研究一下jqury的框架思想,同时自己也可以模仿封装。下面我们学习,封装选择器的思想。

document.getElementById()和标签选择器时document.getElementsByTagName(),这两个属性并没有问题,然而类选择器 document.getElementsByClassName(),但是类选择器并不是所有浏览器兼容,也就是说具有兼容性,所以需要单独对他做处理;首先我们封装一个可以得到id选择器的方法:


function getId(idName) {
//创建一个空数组,将获取的id ,push进数组中存放;
 var result = [];
    var id = document.getElementById(idName);
    if (id != null) {
        result.push(id);
    }
    return result;
}


标签选择器的封装:

//标签
function getTags(tagName) {
    var result = [];
    result.push.apply(result, document.getElementsByTagName(tagName));
    return result;
};

此时将得到的数组也使用了push方法,但是使用了apply();为什么呢?我们简单介绍一下apply方法,

apply() 方法。调用 apply() 方法时,第一个参数是 obj,也就是所要使用的对象,第二个参数是由两个字符串构成的数组,因为document.getElementByClassName()得到的是一个数组,此时你需要的是将得到的数组添加到缓存的数组result中,你需要得到的是标签数组中的每一项,而不是这个数组,apply()的第一个参数是this的指向,就是对象的指向,如果是null的话,那么此时的对象就是window,所以我们必须指明。此时需要注意的是,apply的机制中的第二个参数,内存机制已经对它使用了隐式迭代,所以我们就不必担心遍历的问题。

最后这句代码的意思就是:将得到标签数组的每一个元素添加到result这个对象数组中;

result.push.apply(result, document.getElementsByTagName(tagName));




接下来我们展示类选择器的封装:

var support = {getElementsByClassName: false};




function getClass(className) {
                var result = [];
                if (support.getElementsByClassName === true) {
                    result.push.apply(result, document.getElementsByClassName(className));

                } else {
                   //找到所有的标签;
                    var tags = getTags("*");
                   //遍历所有的标签
                    for (var i = 0, len = tags.length; i < len; i++) {                      
                    var tagClassName = tags[i].className;//"content c2 c3";

                    if (tagClassName) {
                        var changeName = " " + tagClassName + " ";//" content c1 c2 "
                        var chageParam = " " + className + " ";//" content "

                        if (changeName.charAt(chageParam >= 0)) {
                            result.push(tags[i]);
                        }
                    }
                       
                    }
                }
                return result;
            };



看的出来,类选择器比较复杂,因为需要兼容浏览器,看起来比较复杂,接下来,我们就进行分解:

首先我们定义了一个

var support = {getElementsByClassName: false};



这句代码的作用是,我们默认浏览器都不兼容document.getElementsClassName()这个方法,如果浏览器兼容的话,那么直接进行

result.push.apply(result, document.getElementsByClassName(className));

如果不兼容的话,那么我们就需要对他做出兼容处理,首先我们使用了之前封装好的 getTag()方法;使用“*”通配符,得到了页面上所有的标签元素,将返回的数组进行循环遍历,


var tags = getTags("*");

此时我们得到了页面上每一个标签元素,通过判断标签className属性,也就是说我们要找到标签中有 class=“”的元素,


tags[i].className;


如果存在的话,那么我们将对他做出前后添加空格的操作:


var changeName = " " + tagClassName + " ";//" content c1 c2 "
 var chageParam = " " + className + " ";//" content "

为什么要这样做呢?这只是其中一个简单的方法而已,当然也有其他的方法;我们将所要找的类名 与遍历出来的每一个具有类属性的进行比较,是否出现在遍历出来的元素中,我们需要利用charAt(),判断是否查找得到,

if (changeName.charAt(chageParam >= 0)) {
        result.push(tags[i]);
    }



以上,我们封装了所有的选择器,但是并不完美,但是我们此时想要达到的效果输入随意的选择器,就可以进行匹配.

很明显我们需要多各个选择器进行判断,id选择器是#开头,类选择器是  "."(点)开头,所以我们需要判断用户输入的字符串特征就可以调用先关的方法,但我们要注意的是,输入的是String字符串,下面我们进行封装:


function $get(selector) {
    var result;
    if (typeof selector === "string") {
        //id:"#content" class:".header" 标签:"p","div"
        // 各类选择器标识不一样,所以要进行判断
        if (selector.charAt(0) === "#") {//此处判断id选择器
            result = getId(selector.slice(1));
        } else if (selector.charAt(0) === ".") {
            result = getClass(selector.slice(1));
        } else {
            result = getTags(selector);
        }

    }
    return result;
}

有的同学已经看到,输入的字符串使用了slice()方法,提取字符串需要的部分,因为此时我们不需要#和点;

以上代码,我们进行了选择器的封装;有很多的不足,例如要进行查找指定元素下的选择器,选择器的分组和层次,

我们都没有完成,后续我们将对他进行再次的优化。