使用jQuery.form外掛,實現完美的表單非同步提交

weixin_34015860發表於2013-11-05

使用jQuery.form外掛,實現完美的表單非同步提交

傳送門:非同步程式設計系列目錄……

         時間真快,轉眼一個月快結束了,一個月沒寫部落格了!手開始生了,怎麼開始呢……

 

示例下載:使用jQuery.form外掛,實現完美的表單非同步提交.rar

 

抓住6月份的尾巴,今天的主題是

今天我想介紹的是一款jQuery的外掛:Jquery.form.js 官網

         通過該外掛,我們可以非常簡單的實現表單的非同步提交,並實現檔案上傳、進度條顯示等等。

         現在我們從一個ASP.NET同步表單提交開始,然後再將其轉化為非同步的表單提交。我寫了3種表單提交示例,並簡單分析了各種方式的利弊。

當然主題還是使用jQuery表單外掛輕鬆實現表單非同步提交以及分析下該外掛的原始碼。 

 

ASP.NET伺服器端控制元件實現同步表單提交

         ASP.NET伺服器控制元件最大特徵就是標籤包含ID和runat=”server”屬性,在客服端頁面內容中會輸出類似<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGR/8ZxOm5Tn0sfHNJaqE12hKqqJTQ==">標籤,用於儲存控制元件值資料。如下:

    <form runat="server" id="server_form" method=”post”>
        <table border="1">
            <tr>
                <td>使用者名稱:</td>
                <td>
                    <asp:TextBox ID="txtLoginName" runat="server" AutoPostBack=”true” ></asp:TextBox>
                </td>
            </tr>
            <tr>
                <td colspan="2" style="text-align: center">
                   <asp:Button ID="btnSubmit" runat="server" Text="伺服器控制元件同步提交模式" />
                   <asp:Button ID="btnUnSubmit" runat="server" OnClientClick="return false;" Text="不提交表單" /> 
                </td>
            </tr>
        </table>
    </form>
    <asp:Label ID="labResponse" runat="server"></asp:Label>

我們用ASP.NET伺服器控制元件構建了一個表單,在ASP.NET頁面中有這樣限定:

1)         一頁只能有一個伺服器端 Form 標記,其他伺服器端控制元件都在該表單中。

2)         頁面中伺服器端Form中任何導致頁面回發的伺服器端控制元件事件都會觸發表單提交事件submit。比如:

a)         單擊沒有在OnClientClick事件中return false的伺服器端按鈕控制元件

b)         將AutoPostBack屬性設定為true的TextBox、RadioButton、CheckBox、DropDownList等伺服器端控制元件的值改動時都會觸發頁面回發。

c)         另外:type=”submit”的客服端標籤<input type=”submit” />導致表單提交

此方案優勢:

1)         我們在後臺可以非常輕易的獲取伺服器端控制元件的值,比如使用this. txtLoginName.Text訪問控制元件的值或根據表單提交方式在this.Context.Request中獲取表單元素值。

2)         我們在後臺可以輕鬆設定頁面伺服器端控制元件的值,比如使用this. labResponse.Text = “表單提交成功”。

此方案劣勢:

         劣勢很明顯,效率低下,每一次導致的頁面回發都會觸發完整的ASP.NET頁面生命週期,造成出現“白頁”的情況。(更多描述請看:ASP.NET程式設計模型之ASP.NET頁面生命週期圖解

 

         

 

jQuery非同步提交表單

現在我們已經意識到使用同步方式提交表單會造成出現“白頁”的糟糕使用者體驗,那好,現在我使用上一篇分享的技術《觸碰jQuery:AJAX非同步詳解》來將上面同步提交表單調整為非同步提交表單的方式。

    <form id="form1" method="post">
        <table border="1">
            <tr>
                <td>使用者名稱:</td>
                <td>
                    <input type="text" name="loginName" /></td>
            </tr>
            <tr>
                <td>愛  好:</td>
                <td>
                    <input type="checkbox" name="cbLoveYy" value="1" />游泳
                    <input type="checkbox" name="cbLoveYx" value="1" />遊戲
                    <input type="checkbox" name="cbLovePs" value="1" />爬山
                </td>
            </tr>
            <tr>
                <td colspan="2" style="text-align: center">
                    <input id="btnAjaxSubmit" type="submit" value="jQuery.ajax提交" />
                </td>
            </tr>
        </table>
    </form>

jQuery提交程式碼如下:

    <script type="text/javascript">
        $(document).ready(function () {
            $("#btnAjaxSubmit").click(function () {
                var options = {
                    url: 'async_submit_test1.aspx?action=SaveUserInfo',
                    type: 'post',
                    dataType: 'text',
                    data: $("#form1").serialize(),
                    success: function (data) {
                        if (data.length > 0)
                            $("#responseText").text(data);
                    }
                };
                $.ajax(options);
                return false;
            });
        });
    </script>

我們通過$("#form1").serialize()將表單元素的資料轉化為字串,然後通過$.ajax()執行非同步請求資源。

方案:jQuery.ajax() + .aspx請求

此方案優勢:

1)         我們不會感覺頁面的“閃一閃”效果

2)         我們不會因為伺服器耗時響應而導致出現“百頁”的糟糕使用者體驗。

此方案劣勢:

1)         此方案中我還是使用了aspx頁面去響應請求,只是在後臺通過action引數去做相應處理,儘管是非同步操作但卻完完整整的跑了一遍ASP.NET頁面生命週期(這也是在Response.Write()輸出完自己的東西后必須呼叫Response.End();來提前終止生命週期,否則頁面資訊也會一起返回)

2)         jQuery庫提供的序列化表單字串方法不能收集檔案上傳的表單元素,如,$("#form1").serialize()。所以對於包含檔案上傳的表單我們還需通過<iframe>模擬非同步表單提交。(<iframe>模擬非同步表單提交的過程我將在分析jQuery.form外掛的原始碼小節進行說明)

(jQuery庫提供的序列化字串的資料來源時表單的elements屬性,而<input type=”file” />的表單元素不包含在elements中)

 

         當然jQuery.ajax()也可以結合.ashx檔案(一般處理檔案)或其他方式實現高效非同步請求,這邊只是為了說明:非同步請求aspx頁面也會跑一邊aspx頁面生命週期的事實。

 

jQuery.form外掛輕鬆實現表單提交

         現在我們使用jQuery的表單外掛Jquery.form.js(官網)來實現非同步表單提交。

1)         該外掛需要Jquery最低版本為v1.5

2)         該外掛提供了ajaxSubmit和ajaxForm兩種表單提交方式,但不要對同一個表單同時使用兩種方式。

現在我將通過“jQuery+jQuery.form外掛+ashx(一般處理檔案)”來實現一個高效的非同步表單提交。

    <form id="form1" action="ajaxOperation.ashx" method="post">
        <table border="1">
            <caption>jQuery.form.js非同步提交方式</caption>
            <tr>
                <td>使用者名稱:</td>
                <td>
                    <input type="text" name="loginName" /></td>
            </tr>
            <tr>
                <td colspan="2" style="text-align: center">
                    <button id="btnAjaxSubmit">ajaxSubmit提交</button>
                    &nbsp;
                    <input id="btnAjaxForm" type="submit" value="ajaxForm提交" />
                </td>
            </tr>
        </table>
    </form>

1)         為<form>標籤指定action值,指定使用ajaxOperation.ashx處理該表單請求。

2)         使用兩個提交按鈕btnAjaxSubmit和btnAjaxForm分別對應jQuery.form外掛提供的兩種表單提交API。

jQuery表單外掛提交程式碼如下:

    <script type="text/javascript">
        $(document).ready(function () {
        var options = {
            success: function (data) {
                $("#responseText").text(data);
            }
        };

            // ajaxForm
            $("#form1").ajaxForm(options);

            // ajaxSubmit
            $("#btnAjaxSubmit").click(function () {
                $("#form1").ajaxSubmit(options);
            });
        });
    </script>

方案:jQuery.form.js外掛 + .ashx請求

         此方案優勢:

1)         簡簡單單幾句程式碼,我們就可以實現表單的提交,並且可靈活通過ajaxSubmit()函式基於任何事件的觸發實現表單非同步提交。

2)         支援檔案上傳功能,並在新瀏覽器中支援進度條更新。(在jQuery.form外掛原始碼分析中會進行說明)

3)         與jQuery庫完美結合,支援jQuery.ajax()函式觸發的各種事件,支援jQuery.ajax()中所傳遞的引數。(在jQuery.form外掛原始碼分析中會進行說明)

          

         好了,這樣短而易讀的程式碼,這樣的偷懶方式不正是我們追求的嗎?那jQuery.form外掛提供的表單提交API是否高效呢?內部又做了些什麼?接下來跟著我看看jQuery.form外掛內部實現吧。。。

 

jQuery.form外掛原始碼分析

         jQuery.form外掛(Jquery.form.js 官網),可以讓我們非常簡單的實現表單的非同步提交、實現檔案上傳、進度條顯示等等功能。

 

  1. $(“form1”).ajaxSubmit(options)

1)         ajaxSubmit是jQuery表單外掛核心函式。非常靈活,因為它依賴於事件機制,只要有事件觸發就能使用ajaxSubmit()提交表單,eg:超連結、圖片、按鈕的click事件。

2)         options引數是

a)         一個函式,則為表單提交成功後呼叫的回撥函式,即,options={success:function}。

b)         options引數是一個集合,一個引數鍵值對

鍵名

描述

type

(預設為表單的method屬性值,若未設定取GET)

請求的型別,例如:POST、GET、PUT及PROPFIND。大小寫不敏感。

url

(預設取表單的action屬性值,若未設定預設取window.location.href)

請求的URL地址,可以為絕對地址也可以為相對地址。

data

(物件成員必須包含name和value屬性)提供額外資料物件,通過$.param()函式返回序列化後的字串,稍後會拼接到表單元素序列化的字串之後。

extraData

(此引數無需外部提供,由內部處理)

此引數是data在進行序列化成字串之前的一個拷貝,只用於在表單包含<input type=”file” />並且是老瀏覽器。

因為在老瀏覽器中檔案上傳檔案我們需要通過<iframe>來模擬非同步提交,此時extraData會轉變為<input type=”hidden” />元素包含在表單中,被一起提交到伺服器。

dataType

一般不需自己設定。引數作用請看:《jQuery.ajax()-dataType》

traditional

如果你想要用傳統的方式來序列化資料,那麼就設定為true。請參考$.param()深度遞迴詳解

delegation

(適用於ajaxForm)ajaxForm支援Jquery外掛的委託方式(需要Jquery v1.7+),所以當你呼叫ajaxForm的時候其表單form不一定存在,但動態構建的form會在適當的時候呼叫ajaxSubmit。Eg:

        $('#myForm').ajaxForm({ 
            delegation: true,
            target: '#output'
        });  

replaceTarget

(預設:false)與target引數共同起作用,True則執行replaceWirh()函式,false則執行html()函式

target

提供一個Html元素,在請求“成功”並且未設定dataType引數,則將返回的資料replaceWith()或html()掉物件原來的內容,再遍歷物件呼叫success回撥函式。

    if (!options.dataType && options.target) {
        var oldSuccess = options.success || function(){};
        callbacks.push(function(data) {
            var fn = options.replaceTarget ? 'replaceWith' : 'html';
            $(options.target)[fn](data).each(oldSuccess, arguments);
        });
    }

includeHidden

在請求成功後,若設定執行clearForm()函式清空表單元素則會根據includeHidden設定決定如何清空隱藏域元素。

1)         傳遞true,表示清空表單的所有隱藏域元素。

2)         傳遞字串,表示清空特殊匹配的隱藏域表單元素,eg:$('#myForm').clearForm('.special:hidden'),清空class屬性包含special值的隱藏域

clearForm

請求成功時觸發(同success),並用options. includeHidden做為回撥函式引數。

回撥函式:$form.clearForm(options.includeHidden);

resetForm

請求成功時觸發(同success)。

回撥函式:$form.resetForm()

semantic

布林值,指示表單元素序列化時是否嚴格按照表單元素定義順序。

在序列化只有<input type=”image” />元素會放在序列化字串的最後,若semantic=true,則會按照它的定義順序進行序列化。

若你伺服器嚴格要求表單序列化字串的順序,則使用此引數進行控制。

iframe

(預設:false)若有檔案上傳'input[type=file]:enabled[value!=""]',指示是否應該使用<iframe>標籤(在支援html5檔案上傳新特性的瀏覽器中不會使用iframe模式)

iframeTarget

指定一個現有的<iframe>元素,否則將自動生成一個<iframe>元素以及name屬性值。若現有的<iframe>元素沒有設定name屬性,則會自動生成一個name值

iframeSrc

為<iframe>元素設定src屬性值

 

回撥函式

beforeSerialize

提供在將表單元素序列化為字串之前,處理表單元素的回撥函式。

簽名:function(form,options)

函式說明:當前表單物件、options引數集合

返回值:返回false,表示終止表單提交操作。

beforeSubmit

提供在執行表單提交之前,處理資料的回撥函式。

簽名:function(a,form,options)

函式說明:通過formToArray(options.semantic, elements)返回的表單元素陣列、當前表單物件、options引數集合

返回值:返回false,表示終止表單提交操作。

3)         $(“form1”).ajaxSubmit(options) 內部直接或模擬jQuery.ajax(options)非同步提交,所以也直接支援jQuery.ajax(options)所能處理的引數,並且支援jQuery.ajax(options)過程中所觸發的5個區域性事件及6個全域性事件

4)         $(“form1”).ajaxSubmit(options) 內部將內部直接呼叫jQuery.ajax(options)返回的jqxhr物件或模擬的jqxhr物件進行了快取,所以我們可以通過$(“#form1”).data(‘jqxhr’)獲取到本次提交生成的jqxhr物件。

5)         更多jQuery.ajax()函式介紹請看:《觸碰jQuery:AJAX非同步詳解》

 

ajaxSubmit函式處理流程:

1)         根據<form action=”” method=””>處理url、type引數以及success、iframeSrc等引數。

2)         觸發beforeSerialize()回撥函式

3)         序列化data引數和表單元素

4)         觸發beforeSubmit()回撥函式

5)         根據type引數處理options.data和options.url引數

6)         註冊resetForm()和clearForm()回撥函式

7)         註冊將返回資料載入到options.target指定的元素上的回撥函式

8)         註冊success回撥函式,若有options.target則迴圈該元素,併為每個子元素註冊success回撥函式

9)         處理<input type=”file” />檔案上傳元素

a)         不包含檔案元素,直接呼叫jQuery.ajax()函式。

b)         包含檔案元素,並且不支援XMLHttpRequest Level 2提供的檔案上傳新特性window.FormData。則通過IFrame模擬表單非同步提交

  1.                                        i.              呼叫fileUploadIframe()函式。
  2.                                      ii.              根據options. iframeTarget設定決定是建立一個<iframe>元素還是使用現有的<iframe>元素
  3.                                     iii.              模擬xhr物件以及jQuery.ajax()過程,以支援xhr物件返回和ajax事件觸發
  4.                                     iv.              設定<form>的target指向<iframe>元素、encoding和enctype為“multipart/form-data”、method為”post”值等等
  5.                                      v.              處理options.extraData為<input type=”hidden” />元素並新增到<form>元素中。
  6.                                     vi.              呼叫<form>的submit()事件。(同步提交,但因為<form>的target指向<iframe>標籤,所以重新整理的是<iframe>中的內容,以此模擬非同步提交)

c)         包含檔案元素,並且支援XMLHttpRequest Level 2提供的新特性,則呼叫fileUploadXhr()函式,通過FormData()物件將資料傳遞給options.data引數,再呼叫jQuery.ajax(options)函式非同步提交表單。並且XMLHttpRequest Level 2的新特性還支援進度條提示功能。(更多新特性請看:《XMLHttpRequest Level 2 使用指南》

10)     將內部jqxhr快取起來,以供訪問。$form.removeData('jqxhr').data('jqxhr', jqxhr);

11)     返回表單元素本身,以便符合jQuery的鏈式操作模式。

 

  1. $(“form1”).ajaxForm(options)

是對$(“any”).ajaxSubmit(options)函式的一個封裝,適用於表單提交的方式(注意,主體物件是<form>),會幫你管理表單的submit和提交元素([type=submit],[type=image])的click事件。在出發表單的submit事件時:阻止submit()事件的預設行為(同步提交的行為)並且呼叫$(this).ajaxSubmit(options)函式。

ajaxForm支援Jquery外掛的委託方式(需要Jquery v1.7+),所以當你呼叫ajaxForm的時候其表單form不一定存在,ajaxSubmit將在適當的時候呼叫。Eg:

        $('#myForm').ajaxForm({ 
            delegation: true,
            target: '#output'
        }); 

         另外:如果你翻看原來碼你可能會發現這樣的繫結程式碼:.bind('submit.form-plugin', options, doAjaxSubmit),即submit事件名後面有個”. form-plugin”。這是jQuery事件名稱空間語法,作用是方便事件的管理。

 

檔案上傳示例(被jQuery.form外掛封裝的相當簡單,既然寫了就也貼出來吧)

    <form id="form1" action="ajaxOperation.ashx?Action=formUpload" method="post" enctype="multipart/form-data">
        <table>
            <tr>
                <td>附件名字:</td>
                <td>
                    <input type="text" name="fileName" /></td>
            </tr>
            <tr>
                <td>附件:</td>
                <td>
                    <input type="file" name="document" /></td>
            </tr>
            <tr>
                <td colspan="2" style="align-content: center">
                    <input type="submit" value="模擬iframe提交表單" />
                </td>
            </tr>
        </table>
    </form>
    <label id="responseText"></label>

         提交程式碼:

    <script type="text/javascript">
        $(function () {
            var options = {
                success: function (data) {
                    $("#responseText").text(data);
                }
            };

            $("#form1").ajaxForm(options);
        });
    </script>

 

  1. $(“form1”).ajaxFormUnbind()

                   取消$(“”).ajaxForm(options)函式對指定表單繫結的submit和click事件。

                                                                             

  1. $(“form1”).formToArray(semantic,elements)

                   序列化當前表單元素到一個陣列中,每個陣列元素都是包含name和value屬性的物件。返回值是內部構件的一個陣列元素,而elements引數將包含除<input type=”image”>以外的所有表單元素。

 

  1. $(“form1”).formSerialize(semantic)

將表當前單元素序列化為字串形式。

實現如下:

$.fn.formSerialize = function(semantic) {
    return $.param(this.formToArray(semantic));
};

 

  1. $(“form1”).fieldSerialize(successful)  

                   序列化包含name屬性的表單元素為一個字串。Successful引數標識是否獲取type為reset、button、checkbox、radio、submit、image值得元素以及<select>的值。返回$(el).val()。

 

  1. $(“form1”).fieldValue(successful) 或 $.fieldValue(element, successful)

獲取指定表單中的表單元素或指定表單元素的值。Successful引數標識是否獲取type為reset、button、checkbox、radio、submit、image值得元素以及<select>的值。返回$(el).val()。

 

  1. $(“form1”).clearForm(includeHidden)

清空當前表單中input、select、textarea元素的值。includeHidden設定決定如何清空隱藏域元素。

a)         傳遞true,表示清空表單的所有隱藏域元素。

b)         傳遞字串,表示清空特殊匹配的隱藏域表單元素,eg: $('#myForm').clearForm('.special:hidden'),清空class屬性包含special值的隱藏域

 

  1. $.(“form1”).clearFields(includeHidden) 和 $.(“form1”).clearInputs(includeHidden)

作用相同,清空當前表單中所有表單元素的指。includeHidden設定決定如何清空隱藏域元素。

a)         傳遞true,表示清空表單的所有隱藏域元素。

b)         傳遞字串,表示清空特殊匹配的隱藏域表單元素,eg: $('#myForm').clearForm('.special:hidden'),清空class屬性包含special值的隱藏域

 

  1. $(“form1”).resetForm()

重置當前表單元素,導致所有表單元素重置到它的初始值。

 

  1. $(“form1”).selected(select)

將當前表單元素中所有checkbox、radio設定為select。select引數為布林值。

 

 

 

         本文到此結束,通過此博文相信各位,

  1. 再也不會去寫齷齪的同步提交和使用aspx進行非同步相應了。
  2. 再也不用煩惱表單提交過程中各個控制元件的值如何獲取的問題,並且通過jQuery.form表單外掛輕鬆實現表單非同步提交、檔案上傳及進度條顯示。
  3. 清楚的認識了jQuery.form表單在給我們提供便利的背後到底做了什麼手腳(原始碼分析)。

 

 

         感謝大家的收看,感覺不錯還請多幫推薦推薦推薦……

 

 

 

相關文章