• 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框架设计(四)开始进入组合选择器.

  • 相关阅读:
    [转]char、varchar、nchar、nvarchar的区别
    【转】Asp.net 2.0中页的生存周期(Lifecycle)和动态控件 [.Net]
    git免登录sshkey
    ios8,xcode6 周边
    iOS 推送证书
    Lazarus中TreeView导出XML以及XML导入TreeView
    flac文件提取专辑封面手记
    Lazarus解决含中文文件名或路径的使用问题
    使用PowerShell管理Windows8应用
    thbgm拆包【in progress】
  • 原文地址:https://www.cnblogs.com/likeFlyingFish/p/5727140.html
Copyright © 2020-2023  润新知