zoukankan      html  css  js  c++  java
  • JavaScript框架设计(三) push兼容性和选择器上下文

    JavaScript框架设计(三) push兼容性和选择器上下文

    博主很久没有更博了.

    在上一篇 JavaScript框架设计(二) 中实现了最基本的选择器,getId,getTaggetClass ,并且解决了document.getElementsByClassName() 这个函数在ie8下面的兼容性问题,似乎以前的代码已经没问题了? 但是, 注意这一句代码: result.push.apply(result,document.getElementsByTageName),result.push.apply 方法接受的是一个伪数组,由于apply要求第一个参数之后的参数必须用数组包装起来,因此我们巧妙地使用apply对后面的 document.getElementsByTageName 伪数组进行展开.但是,ie8下面是不行的,在ie8里面,这里只能接受真数组.

    所以我们实现一个自己的push

    02.js

    var myPush = function (target, eles) {
        var j = target.length,
            i = 0;
        while (target[j++] = eles[i++]) { };
        target.length = j - 1;
    }
    

    所以我们原来的代码可以改成这样:

    var getTag = function (tag, result) {
        result = result || [];
        try {
            result.push.apply(result, document.getElementsByTagName(tag));
        }
        catch (e) {
            myPush(result, document.getElementsByTagName(tag)); 
        }
        return result;
    }
    

    似乎这样是可以的,但是 由于try catch是错误异常处理,是非常消耗性能的!!!,所以,要做就只能做一次,然后将Array原型上面的push改为我们自己的push 那么我们要做的就是和document.getElementsByClassName() 一样,首先便检测它是否可用.

    那么我们修改我们的push方法

    common.js

    push = {
        apply: function (target, eles) {
            var j = target.length,
                i = 0;
            while (target[j++] = eles[i++]) { };
            target.length = j - 1;
        }
    }
    

    为什么要这么做? 因为push方法一定会有一个apply调用模式,我们用这个对象来模拟push.apply操作.

    然后,看下面的代码

    try {
        var div = document.createElement('div');
        div.innerHTML = "<div></div><div></div>";
        var divs = div.getElementsByTagName('div');
        var result = [];
        result.push.apply(result, divs);
    } catch (e) {
        Array.prototype.push = push;
    }
    

    这样一来,如果push报错,那么我们就直接在Array的原型的push方法替换为我们自己的方法,以后所有的调用都不会再报错了.


    好了,push的兼容性问题已经解决,下面开始进入选择器的上下文问题

    我们将以前所有的代码都整合,放在common.js里面,后面所有的修改都是在common$.js上面修改.

    实现查找某一个dom对象下的元素

    我们想实现一个功能:

    get(selector,父元素) 即在这个父元素下查找指定元素.父元素可以是dom对象,dom数组,选择器字符串.

    第一步,我们假定父元素就是dom对象.

    那么,我们要先修改的就是 getId,getTag,getClass 这三个方法.

    修改如下,

    common1.js

    var getTag = function (tag, context, result) {
        result = result || [];
        context = context || document;
        result.push.apply(result, context.getElementsByTagName(tag));
        return result;
    }
    
    var getId = function (id, result) {
        result = result || [];
        result.push(document.getElementById(id));
        return result;
    }
    
    var getClass = function (className, context, result) {
        result = result || [];
        context = context || document;
        // 首先判断我们的docoument.getElementsByClassName() 有没有这个功能
        var res;
        if (support.getElementsByClassName) {
            res = context.getElementsByClassName(className);
        } else {
            // 自己实现getElementByClassName
            // 思路 : 首先获得所有元素,然后再在所有元素中获得带有这个类的元素
            res = myGetByClassName(className, context);
    
        }
        result.push.apply(result, res);
        return result;
    }
    
    

    注意,由于context用的会比results多,所以我们可以将context放置为第二个参数.

    而getElementById() 这个方法只有document对象有,所以我们不考虑.

    然后我们修改get方法:

    var get = function (selector, context, result) {
        result = result || [];
        context = context || document;
        var rquickExpr = /^(?:#([w-]+)|.([w-]+)|([w]+)|(*))$/,
            // var rquickExpr = /^(?:  (#[w-]+)  |  (.[w-]+)  |  ([w]+)  |  (*) )$/;
            m = rquickExpr.exec(selector);
        if (!m) {
            return result;
        }
        if (m[1]) {
        // 注意 在这里全部换位context
            result = getId(m[1], result);
        } else if (m[2]) {
            result = getClass(m[2], context, result);
        } else {
            result = getTag(m[3] || '*', context, result)
        }
        return result;
    };
    

    下面是测试代码

    03.html

        <style>
            .c div {
                border: 1px solid green;
                height: 50px;
                 300px;
                margin-bottom: 30px;
            }
            
            .c1 {
                border: 1px solid green;
                height: 50px;
                 300px;
                margin-bottom: 30px;
            }
        </style>
    </head>
    
    <body>
        <div class="c">
            <div></div>
            <div></div>
        </div>
        <hr>
        <div>
            <div class="c1"></div>
        </div>
        <script src="common1.js"></script>
        <script>
            onload = function(){
                each(get('div',get('.c')[0]),function(){
                    this.style.backgroundColor = "green";
                });
            }
        </script>
    </body>
    

    下面是效果

    先睡觉了,明天继续.


    上下文context 为一个dom数组

    好了,上面已经解决了上下文是一个dom对象的问题,但是这样有一个问题,就是我每次只能查询一个元素下面的,如果我想查找多个呢?

    很简单,如果上下文是一个数组,我们可以对这个数组进行循环,对数组中的每一个元素执行上面的操作.

    那么,由于 我们单独的get方法都是通过通用的get方法来调用,那么这个操作只需要在通用的get方法里面完成就可以了.

    common2.js

    var get = function (selector, context, result) {
        result = result || [];
        context = context || document;
        // 如果不是数组,那我们就让它成为一个数组.
        if (context.nodeType) {
            context = [context];
        }
        var rquickExpr = /^(?:#([w-]+)|.([w-]+)|([w]+)|(*))$/,
            // var rquickExpr = /^(?:  (#[w-]+)  |  (.[w-]+)  |  ([w]+)  |  (*) )$/;
            m = rquickExpr.exec(selector);
        each(context, function (v) {
            if (!m) {
                return;
            }
            if (m[1]) {
                result = getId(m[1], result);
            } else if (m[2]) {
                result = getClass(m[2], this, result);
            } else {
                result = getTag(m[3] || '*', this, result)
            }
        })
    
        return result;
    };
    
    

    注意这一步非常巧妙

        if (context.nodeType) {
            context = [context];
        }
        // 如果不是数组,我们就让它成为一个数组.
    

    然后在单独的get方法里面对context的判断可以去掉了,因为get方法调用的时候一定会传入一个dom对象作为上下文.

    common2.js

    var getTag = function (tag, context, result) {
        result = result || [];
        result.push.apply(result, context.getElementsByTagName(tag));
        return result;
    }
    
    var getId = function (id, result) {
        result = result || [];
        result.push(document.getElementById(id));
        return result;
    }
    
    var getClass = function (className, context, result) {
        result = result || [];
        // 首先判断我们的docoument.getElementsByClassName() 有没有这个功能
        var res;
        if (support.getElementsByClassName) {
            res = context.getElementsByClassName(className);
        } else {
            // 自己实现getElementByClassName
            // 思路 : 首先获得所有元素,然后再在所有元素中获得带有这个类的元素
            res = myGetByClassName(className, context);
    
        }
        result.push.apply(result, res);
        return result;
    }
    
    

    下面是测试代码:

    04.html

        <style>
            .c div {
                border: 1px solid green;
                height: 50px;
                 300px;
                margin-bottom: 30px;
            }
            
            .c1 {
                border: 1px solid green;
                height: 50px;
                 300px;
                margin-bottom: 30px;
            }
        </style>
    </head>
    
    <body>
        <div class="c">
            <div></div>
            <div></div>
        </div>
        <hr>
        <div>
            <div class="c1"></div>
        </div>
        <script src="common2.js"></script>
        <script>
            onload = function(){
                each(get('div',get('.c')),function(){
                    this.style.backgroundColor = "green";
                });
                each(get('.c1',get('div')),function(){
                    this.style.backgroundColor = "yellow";
                })
            }
        </script>
    </body>
    
    

    测试结果,前面两个div变绿,最后一个变黄

    是不是欧了


    context 上下文是一个字符串

    好了,上面我们对父元素是一个dom对象和dom数组都做了判断,可是我们在使用jQuery时候,我们常常在这里传递一个字符串,因此,我们在这里对字符串的额情况也做一个分析.

    其实,很简单,判断是不是字符串,如果是字符串,那么就递归调用get方法.

    仅仅加上两句代码

    看下面的get方法

    common3.js

        if (typeof context ===  'string') {
            context = get(context);
        }
    

    好了,写个页面测试一下:

    
    

    这个页面应该和上面页面得到一模一样的结果,是不是这样呢?

    好了,选择器上下文的问题已经解决了.下来JavaScript框架设计(四)开始进入组合选择器.

  • 相关阅读:
    【Codechef】Chef and Bike(二维多项式插值)
    USACO 完结的一些感想
    USACO 6.5 Checker Challenge
    USACO 6.5 The Clocks
    USACO 6.5 Betsy's Tour (插头dp)
    USACO 6.5 Closed Fences
    USACO 6.4 Electric Fences
    USACO 6.5 All Latin Squares
    USACO 6.4 The Primes
    USACO 6.4 Wisconsin Squares
  • 原文地址:https://www.cnblogs.com/likeFlyingFish/p/5727140.html
Copyright © 2011-2022 走看看