1. 程式人生 > >從零開始學習jQuery (九) jQuery工具函式

從零開始學習jQuery (九) jQuery工具函式

本系列文章導航

一.摘要

本系列文章將帶您進入jQuery的精彩世界, 其中有很多作者具體的使用經驗和解決方案,  即使你會使用jQuery也能在閱讀中發現些許祕籍.

我們經常要使用指令碼處理各種業務邏輯, 最常見的就是陣列和物件的操作. jQuery工具函式為我們操作物件和陣列提供了便利條件.

二.前言

大部分人僅僅使用jQuery的選擇器選擇物件, 或者實現頁面動畫效果. 在處理業務邏輯時常常自己編寫很多演算法. 本文提醒各位jQuery也能提高我們操作物件和陣列的效率. 並且可以將一些常用演算法擴充到jQuery工具函式中, 實現指令碼函式的複用.

三.什麼是工具函式

工具函式是指在jQuery物件(即變數"$")上定義的函式. 這些函式都是工具類函式.比如C#中最常用的trim()函式:

$.trim("  text   ");


在原始javascript中並沒有提供同時去除前後空格的trim函式. 所以這一類常用的工具函式統稱為 "Utilities" 函式.對應jQuery官方文件:

"$"其實是"window"物件的屬性,  所以下面幾句話是等價的:

 $.trim("  text   ");
window.$.trim("  text   ");
window.jQuery("  text   "
); jQuery.trim(" text ");

四.工具函式分類

工具函式主要分為下面幾類:

  • 瀏覽器及特性檢測
  • 陣列和物件操作
  • 測試操作
  • 字串操作
  • Url操作

區別於前幾章的講解方式, 本文不在列舉函式列表. 大家在應用中, 比如遇到想操作一個字串, 可以首先從在"API文件/Utilities/字串操作"中查詢是否已經提供了快捷的工具函式. 如果沒有再考慮自己開發.

下面使用例項具體的每個分類下常用的工具函式.

五.瀏覽器及特性檢測

jQuery的優秀就在於其跨瀏覽器的特性, 通常我們不用再針對不同瀏覽器書寫不同的程式碼.  但是如果是jQuery開發人員或者外掛開發人員就要自行處理瀏覽器差異, 以便為使用者提供跨瀏覽器的特性.

jQuery提供了下列屬性用於獲取瀏覽器特性:

在1.3版本中已經廢除了三個屬性, 這裡不再講解.  讓我們將注意力放在 jQuery.support函式上.

jQuery.support

說明:

jQuery 1.3 新增。一組用於展示不同瀏覽器各自特性和bug的屬性集合。

jQuery提供了一系列屬性,你也可以自由增加你自己的屬性。其中許多屬性是很低階的,所以很難說他們能否在日新月異的發展中一直保持有效,但這這些主要用於外掛和核心開發者。

所有這些支援的屬性值都通過特性檢測來實現,而不是用任何瀏覽器檢測。以下有一些非常棒的資源用於解釋這些特性檢測是如何工作的:

  • http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
  • http://yura.thinkweb2.com/cft/
  • http://www.jibbering.com/faq/faq_notes/not_browser_detect.html
jQuery.support主要包括以下測試:

boxModel: 如果這個頁面和瀏覽器是以W3C CSS盒式模型來渲染的,則等於true。通常在IE 6和IE 7的怪癖模式中這個值是false。在document準備就緒前,這個值是null。

cssFloat: 如果用cssFloat來訪問CSS的float的值,則返回true。目前在IE中會返回false,他用styleFloat代替。

hrefNormalized: 如果瀏覽器從getAttribute("href")返回的是原封不動的結果,則返回true。在IE中會返回false,因為他的URLs已經常規化了。

htmlSerialize: 如果瀏覽器通過innerHTML插入連結元素的時候會序列化這些連結,則返回true,目前IE中返回false。

leadingWhitespace: 如果在使用innerHTML的時候瀏覽器會保持前導空白字元,則返回true,目前在IE 6-8中返回false。

noCloneEvent: 如果瀏覽器在克隆元素的時候不會連同事件處理函式一起復制,則返回true,目前在IE中返回false。

objectAll: 如果在某個元素物件上執行getElementsByTagName("*")會返回所有子孫元素,則為true,目前在IE 7中為false。

opacity: 如果瀏覽器能適當解釋透明度樣式屬性,則返回true,目前在IE中返回false,因為他用alpha濾鏡代替。

scriptEval: 使用 appendChild/createTextNode 方法插入指令碼程式碼時,瀏覽器是否執行指令碼,目前在IE中返回false,IE使用 .text 方法插入指令碼程式碼以執行。

style: 如果getAttribute("style")返回元素的行內樣式,則為true。目前IE中為false,因為他用cssText代替。

tbody: 如果瀏覽器允許table元素不包含tbody元素,則返回true。目前在IE中會返回false,他會自動插入缺失的tbody。

講解:

針對上面眾多的瀏覽器特性屬性,  本文只講解兩個特性.

1.盒式模型 boxModel

下圖是W3C標準中的盒式模型圖:

image

假設如下元素:

<style type="text/css">
.boxModel
{
    width:200px;
    height:50px;
    padding:10px;
    border:solid 5px #FF0000;
    background-color:#acacac;
}
</style>
<div id="divBox" class="boxModel">


顯示效果如圖:

image

在CSS中設定元素寬度為200px, 下面以此元素為例講解盒式模式.

W3C 盒式模型:

元素的寬度和高度為盒式模型圖中的Context部分, 不包括padding, border和margin部分.

目前除了IE所有的瀏覽器都僅支援W3C盒式模型. 在W3C盒式模型中,  示例中包含紅框在內的區域內容寬度為200+2*10+2*5=230px, 高度為50+2*10+2*5=80px.

IE 盒式模型:

設定的寬度包括padding,border. 實際內容寬度content Width = width  - padding – border

在IE5.5及更早的版本中, 使用了此模型. 在更高的IE版本上如果由於某些原因讓瀏覽器執行在怪異模式下則也會使用此盒式模式.所以需要在頁面上宣告正確的DOCTYPE. 有關DOCTYPE請參考此文:

下面是兩種盒式模式的對比:

image

我們可以使用 jQuery.support.boxModel 屬性來獲取瀏覽器是否使用了W3C盒式模型. true表示使用W3C boxModel.

2.浮動樣式

通過javascript指令碼設定元素的float樣式時, IE和FireFox存在不同, IE使用style.styleFloat, FireFox使用style.cssFloat:

div.style.styleFloat = "left"; //IE
div.stlye.cssFloat = "left"; //FF


jQuery.support.cssFloat
屬性返回true則表示可以使用cssFloat來設定float樣式. IE中返回false;

注意, 我們可以通過CSS()方法設定float樣式, jQuery內部會自動幫我們判斷是使用styleFloat還是cssFloat:

$("#divResult").css("float","left"); //相容IE和FF

六.陣列和物件操作

實現UI我們常常操作DOM物件或者jQuery包裝集, 但是實現演算法或者業務邏輯時往往操作的是陣列和物件.

下面講解最常用的陣列和物件相關的工具函式.

1.迭代

返回值:Object

說明:

通用例遍方法,可用於例遍物件和陣列。

不同於例遍 jQuery 物件的 $().each() 方法,此方法可用於例遍任何物件。回撥函式擁有兩個引數:第一個為物件的成員或陣列的索引,第二個為對應變數或內容。如果需要退出 each 迴圈可使回撥函式返回 false,其它返回值將被忽略。

講解:

對於jQuery包裝集我們可以使用each(callback)方法迭代包裝集中的每一個元素. callback是一個會函式, 接受一個引數表示當前訪問物件的索引.

$("img").each(function(i){
   this.src = "test" + i + ".jpg";
 });

注意傳入的第一個引數可以是陣列或者物件.如果陣列,則遍歷陣列中的每一個物件. 第一個引數表示索引,第二個引數表示值, this表示當前遍歷的元素, 可以通過返回false終止迭代, 比如下面的示例遍歷到第二個元素後會終止:

                $.each(["a", "b", "c"], function(i, n)
                {
                    alert("Item #" + i + ": " + n);//可以獲取到i值
                    if (i >= 1)
                    {
                        return false;
                    }
                });
            $("#iterateArray").click(function(event)
            {
                var array = $.each(["a", "b", "c"], function(i, n)
                {
                    alert("Item #" + i + ": " + n ); //第一個引數i表示索引, this表示當前遍歷的物件
                    if (i >= 1)
                    {
                        return false;
                    }
                });
            });


如果傳遞的是物件, 則遍歷物件的每一個屬性, 即使函式返回false也依然會遍歷完所有的屬性, 第一個引數表示屬性key(屬性名稱,是obejct型別),第二個引數表示值,,this表示當前屬性的值:

            $("#iterateObject").click(function(event)
            {
                $.each({ name: "ziqiu.zhang", sex: "male", status: "single" }, function(i, n)
                {
                    alert("Item #" + i.toString() + ": " + n ); //第一個引數i表示屬性的key(object), this表示屬性值
                    if (i >= 1)
                    {
                        return false;
                    }
                });
            });


each將是我們最常使用的函式, 特別注意each雖然迭代每一個元素或屬性, 但是在迭代函式中並不會改變當前元素的值, 也就是無法改變返回後的物件.如果需要改變陣列中的每一個元素並且將結果返回, 因使用函式.

2.篩選

返回值: Array

說明:

使用過濾函式過濾陣列元素。

此函式至少傳遞兩個引數:待過濾陣列和過濾函式。過濾函式必須返回 true 以保留元素或 false 以刪除元素。

講解:

預設invert為false, 即過濾函式返回true為保留元素. 如果設定invert為true, 則過濾函式返回true為刪除元素.

下面的示例演示如何過濾陣列中索引小於 0 的元素:

$.grep( [0,1,2], function(n,i){
  return n > 0;
});

返回的結果是[1,2]

3.轉換

返回值:Array

說明:

將一個數組中的元素轉換到另一個數組中。

作為引數的轉換函式會為每個陣列元素呼叫,而且會給這個轉換函式傳遞一個表示被轉換的元素作為引數。轉換函式可以返回轉換後的值、null(刪除陣列中的專案)或一個包含值的陣列,並擴充套件至原始陣列中。

講解:

1.3.2版本中此函式和each函式已經幾乎相同(以前稍有不同),  現在唯一的區別就是回撥函式可以改變當前元素.返回null則刪除當前元素.

下面是幾個例子:

    var arr = [ "a", "b", "c", "d", "e" ]
    $("div").text(arr.join(", "));

    arr = jQuery.map(arr, function(n, i){
      return (n.toUpperCase() + i);
    });
    $("p").text(arr.join(", "));

    arr = jQuery.map(arr, function (a) { return a + a; });
    $("span").text(arr.join(", "));

4.合併

合併物件是我們常常編寫的功能, 通常使用臃腫的for迴圈來進行.jQuery為我們提供了很多功能的合併函式:

名稱 說明 舉例

用一個或多個其他物件來擴充套件一個物件,返回被擴充套件的物件。

如果不指定target,則給jQuery名稱空間本身進行擴充套件。這有助於外掛作者為jQuery增加新方法。

如果第一個引數設定為true,則jQuery返回一個深層次的副本,遞迴地複製找到的任何物件。否則的話,副本會與原物件共享結構。

為定義的屬性將不會被複制,然而從物件的原型繼承的屬性將會被複制。

合併 settings 和 options,修改並返回 settings:
var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
jQuery.extend(settings, options);


結果:
settings == { validate: true, limit: 5, name: "bar" }

將類陣列物件轉換為陣列物件。

類陣列物件有 length 屬性,其成員索引為 0 至 length - 1。實際中此函式在 jQuery 中將自動使用而無需特意轉換。

將DOM物件集合轉換為陣列:
var arr = jQuery.makeArray(document.getElementsByTagName("div"));
確定第一個引數在陣列中的位置,從0開始計數(如果沒有找到則返回 -1 )。 檢視對應元素的位置:
var arr = [ 4, "Pete", 8, "John" ];
jQuery.inArray("John", arr);  //3
jQuery.inArray(4, arr);  //0
jQuery.inArray("David", arr);  //-1

合併兩個陣列

返回的結果會修改第一個陣列的內容——第一個陣列的元素後面跟著第二個陣列的元素。要去除重複項,請使用$.unique()

合併兩個陣列到第一個陣列上:
$.merge( [0,1,2], [2,3,4] )

結果:
[0,1,2,2,3,4]
刪除陣列中重複元素。只處理刪除DOM元素陣列,而不能處理字串或者數字陣列。 刪除重複 div 標籤:
$.unique(document.getElementsByTagName("div"));

[<div>, <div>, ...]


講解:

上面的函式看著有些混亂.  看看我們以後會常用的.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>jQuery Utilities - jQuery.merge</title>

    <script src="../scripts/jquery-1.3.2-vsdoc2.js" type="text/javascript"></script>

    <script type="text/javascript">
        $(function()
        {
            $("#go").click(function(event)
            {
                $("#divResult").html("");
                var first = [1, 3, 5];
                $("#divResult").append("<span>first:[" + first.join(",") + "]</span>").append("<br/>");
                var second = [2, 4, 6];
                $("#divResult").append("<span>second:[" + second.join(",") + "]</span>").append("<br/>");
                var result = $.merge(first, second);
                $("#divResult").append("<span>result:[" + result.join(",") + "]</span>").append("<br/>");
                $("#divResult").append("<span>first after merged:[" + first.join(",") + "]</span><br/>");
                $("#divResult").append("<span>second after merged:[" + second.join(",") + "]</span><br/>");

            });
        });       
           
    </script>

</head>
<body>
    <button id="go">
        合併陣列</button>
    <br />
    <div id="divResult">
    </div>
</body>
</html>

結果如圖:

image 
另外不能因為有了jQuery就忘記我們的原始javascript. 比merge更常用的其實是join和split函式.

merge函式會改變第一個合併的陣列, 如果是我設計我就不會這麼做. 因為返回值已經是合併後的陣列了.如此設計讓函式產生歧義.

列表中的那麼多函式不再一一講解. 先用先查. 除了 這個不得不提的函式. 下面單提一個小結講解.

5. jQuery.extend

在開發外掛的時候最常用此函式函式來處理options.

下面是fancybox外掛獲取options的程式碼:

settings = $.extend({}, $.fn.fancybox.defaults, settings);

上面的程式碼target是一個空物件, 將預設設定defaults作為第一個物件,  將使用者傳入的設定setting合併到default上,  setting上有的屬性以setting為準. setting沒有傳入的屬性則使用default的預設值. 然後將合併的結果複製給target並作為函式返回值返回.

看一個完整的示例:

var empty = {}
var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var settings = jQuery.extend(empty, defaults, options);


結果:

settings == { validate: true, limit: 5, name: "bar" }
empty == { validate: true, limit: 5, name: "bar" }


target引數要傳遞一個空物件是因為target的值最後將被改變.比如:

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var settings = jQuery.extend(defaults, options);


上面的程式碼將defaults作為target引數,  雖然最後settings的結果一樣, 但是defaults的值被改變了! 而外掛中的預設值應該都是固定! 所以使用時請注意target引數的用法.

下面是我的完整示例和結果:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>jQuery Utilities - jQuery.extend</title>

    <script src="../scripts/jquery-1.3.2-vsdoc2.js" type="text/javascript"></script>

    <script type="text/javascript">
        $.toObjectString = function (obj)
        {
            var result = "{";
            var counter = 0;
            $.each(obj, function(i, n)
            {
                if (counter > 0) { result += ","; }
                result += i.toString() + ":" + n.toString();
                counter++; 
            });
            result += "}";
            return result;
        }

        $(function()
        {
            $("#go1").click(function(event)
            {
                $("#divResult").html("");

                var empty = {}
                var defaults = { validate: false, limit: 5, name: "foo" };
                var options = { validate: true, name: "bar" };

                $("#divResult").append("<span>empty:" + $.toObjectString(empty) + "</span>").append("<br/>");
                $("#divResult").append("<span>defaults:" + $.toObjectString(defaults) + "</span>").append("<br/>");
                $("#divResult").append("<span>options:" + $.toObjectString(options) + "</span>").append("<br/>");                
                
                var settings = jQuery.extend(empty, defaults, options);
                $("#divResult").append("<span>settings after extend:" + $.toObjectString(settings) + "</span>").append("<br/>");
                $("#divResult").append("<span>defaults after extend:" + $.toObjectString(defaults) + "</span>").append("<br/>");
                $("#divResult").append("<span>options after extend:" + $.toObjectString(options) + "</span>").append("<br/>");

            });

            $("#go2").click(function(event)
            {
                $("#divResult").html("");

 
                var defaults = { validate: false, limit: 5, name: "foo" };
                var options = { validate: true, name: "bar" };
                $("#divResult").append("<span>defaults:" + $.toObjectString(defaults) + "</span>").append("<br/>");
                $("#divResult").append("<span>options:" + $.toObjectString(options) + "</span>").append("<br/>");
                
                var settings = jQuery.extend(defaults, options);
                $("#divResult").append("<span>settings after extend:" + $.toObjectString(settings) + "</span>").append("<br/>");
                $("#divResult").append("<span>defaults after extend:" + $.toObjectString(defaults) + "</span>").append("<br/>");
                $("#divResult").append("<span>options after extend:" + $.toObjectString(options) + "</span>").append("<br/>");

            });
        });       
           
    </script>

</head>
<body>
    <button id="go1" style="height:40px;width:400px;">
        jQuery.extend(empty, defaults, options)</button>
    <button id="go2"  style="height:40px;width:400px;">
        jQuery.extend(defaults, options)</button>
    <br />
    <div id="divResult">
    </div>
</body>
</html>

結果:

image

image

七.測試工具函式

測試工具函式主要用於判斷物件是否是某一種型別, 返回的都是Boolean值:

同時別忘記了javascript中自帶的isNaN和isFinite:

var test = "123";
alert(isNaN(test));
alert(isFinite(test));


isNaN函式判斷引數是否是非數字. 如果是數字則返回false.

isFinite函式檢查其引數是否是無窮大.如果引數是 NaN(非數字),或者是正、負無窮大的數,則返回 false.否則返回true.

八.字元處操作工具函式

目前核心類庫中只有一個字串工具函式:

返回值: string

說明:去掉字串起始和結尾的空格。

舉例:

去掉字串起始和結尾的空格:

$.trim("  hello, how are you?  ");


結果:

"hello, how are you?"

九.Url操作工具函式

返回值:string

說明:

將表單元素陣列或者物件序列化。是.serialize()的核心方法。

陣列或jQuery物件會按照name/value對進行序列化,普通物件按照key/value對進行序列化

舉例:

    var params = { width:1680, height:1050 };
    var str = jQuery.param(params);
    $("#results").text(str);

結果:

width=1680&height=1050


jQuery將其歸為Urls分類, 因為此方法通常用於傳送GET請求時將物件作為urls引數傳遞給服務端.

十. 擴充套件工具函式與jQuery包裝集函式

擴充套件工具函式只需要對jQuery(即"$")進行擴充套件. 通常開發工具函式或者外掛的人希望在開發時使用"$", 但因為"$"有可能和其他指令碼庫衝突, 所以通常我們使用下面的語法開發工具函式:

        (function($)
        {
            $.myExtendMethod = function(o)
            {
                alert(0);
            };            
        })(jQuery);

在函式體內的"$"能保證是代表jQuery物件.

然後使用這種方式開發不能享受到智慧感知的便利. 一般我們將擴充套件工具函式和擴充套件jQuery包裝集函式都放在一個單獨的檔案中.

下面這個示例演示如何新增自定義的jQuery工具方法和jQuery包裝集方法:

/// <reference path="jquery-1.3.2-vsdoc2.js" />
jQuery.myExtendMethod = function(o)
{
    ///    <summary>
    ///        擴充套件方法註釋.
    ///    </summary>
    ///    <param name="o" type="String">引數提示文字</param>
    ///    <returns type="string" >返回值提示文字</returns>
    alert(0);
};

jQuery.fn.myExtendMethod = function(o)
{
    ///    <summary>
    ///        擴充套件方法註釋.
    ///    </summary>
    ///    <param name="o" type="String">引數提示文字</param>
    ///    <returns type="string" >返回值提示文字</returns>
    alert(0);
};

通過第一行reference, 我們可以在此js檔案中繼續使用jQuery指令碼智慧感知.

jQuery.myExtendMethod方法擴充套件的工具函式.

jQuery.fn.myExtendMethod方法擴充套件的是jQuery包裝集函式, 即為使用$()獲取到的物件添加了方法.

同理使用XML註釋, 比如<summary> 還可以為自定義方法新增智慧感知提示.指令碼中的XML註釋和.NET中的一樣, 有關.NET中的XML註釋可以參考我的另外一篇文章:

十一.總結

jQuery提供了許多的工具函式,  在一般情況下可以滿足我們的需要. 但是對於像JSON格式化一類的操作, 需要我們自己擴充套件, 現有的各種擴充套件元件資源將提高我們的開發效率, 本系列Ajax章節就介紹的一個JSON序列化的元件jQuery.json. 更多的元件需要大家在工作中挖掘.