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;
            width: 300px;
            margin-bottom: 30px;
        }
        
        .c1 {
            border: 1px solid green;
            height: 50px;
            width: 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;
            width: 300px;
            margin-bottom: 30px;
        }
        
        .c1 {
            border: 1px solid green;
            height: 50px;
            width: 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框架設計(四)開始進入組合選擇器.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM