恰巧與同事討論到將網頁輸入欄位設為唯讀的做法,就順便整理分享一下我嘗試過的幾種方式: readonly, disabledblockUI

我把三種做法整理在一個範例中,設了四個checkbox來啟動不同的唯讀效果進行測試: (四個checkbox有加上radio互斥點選的效果,請參照這篇文章)

唯讀1是readonly法、唯讀2是disabled、唯讀3則是blockUI法。

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>blockUI.block()</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.4.4.js" 
        type="text/javascript"></script> 
    <script src="https://github.com/malsup/blockui/raw/master/jquery.blockUI.js?v2.31" 
        type="text/javascript"></script> 
    <style type="text/css">
        .ro { background-color: #cccccc; color: #444444; }
    </style>
    <script type="text/javascript">
        $(function () {
            //設計t1, t2連動,t1 blur()時清掉t2的值
            $("#t1").blur(function () { $("#t2").val(""); });
            var $ctrlGrp = $(".controller input");
            $ctrlGrp.click(function () {
                //先找出目前checked者
                var $chked = $ctrlGrp.filter(":checked").not(this);
                //取消已選取者的唯讀設定
                if ($chked.length == 1)
                    clearReadOnly($chked[0].id);
                //清除其他選取者,形成互斥
                $ctrlGrp.not(this).removeAttr("checked");
                //點選一律為checked,並設定唯讀
                this.checked = true;
                setReadOnly(this.id);
            });
            //示範三種設定唯讀的方式
            function setReadOnly(t) {
                switch (t) {
                    case "ro1":
                        //設定readonly屬性,缺點是事件仍有作用
                        $("#t1,#t2").addClass("ro").attr("readonly", true);
                        break;
                    case "ro2":
                        //設定disabled的,但欄位外觀較異常,且Submit前不還原
                        //欄位將被忽略不Postback回後端
                        $("#t1,#t2").addClass("ro").attr("disabled", true);
                        break;
                    case "ro3":
                        //利用blockUI block()形成遮罩,阻止使用者對欄位進行操作
                        $("#t1,#t2").addClass("ro");
                        $("#dvEditor").block({
                            message: null,
                            overlayCSS: {
                                backgroundColor: "#cccccc",
                                opacity: 0.2,
                                cursor: "default"
                            }
                        });
                        break;
                }
            }
            //清除唯讀設定
            function clearReadOnly(t) {
                switch (t) {
                    case "ro1":
                        $("#t1,#t2").removeClass("ro").removeAttr("readonly");
                        break;
                    case "ro2":
                        $("#t1,#t2").removeClass("ro").removeAttr("disabled");
                        break;
                    case "ro3":
                        $("#t1,#t2").removeClass("ro")
                        $("#dvEditor").unblock();
                        break;
                }
            }
        });
    </script>
</head>
<body>
<fieldset>
    <legend class="controller">
    正常 <input type="checkbox" id="ro0" />
    唯讀1 <input type="checkbox" id="ro1" /> 
    唯讀2 <input type="checkbox" id="ro2" />
    唯讀3 <input type="checkbox" id="ro3" />
    </legend>
    <div id="dvEditor">
    <input type="text" id="t1" value="" />
    <input type="text" id="t2" value="" />
    </div>
    
</fieldset>
</body>
</html>

來談談三種做法的優缺點:

  1. readonly:
    最直覺簡單,但我故意加上的t1, t2連動突顯它的問題。即使設為readonly,點選t1再移開焦點照樣會觸發blur()事件! 若要避免,則在設定唯讀時得一併停用事件;若唯讀狀態允許動態取消,則又要再加上重新啟用事件的邏輯,挺麻煩的。readonly的另一個缺點是<select>不支援,沒法用統一的做法處理各式輸入欄位。
  2. disabled:
    設定disabled屬性將欄位停用後,使用者就無法對欄位進行任何操作,一方面等同唯讀,也不用擔心事件會被觸發,並且<input>, <select>, <textarea>通通適用,算是很乾脆的選擇。
    不過,停用欄位長相跟一般正常欄位不太一樣(可參考上圖唯讀2的呈現結果,不同瀏覽器差異幅度也各異),或許會影響視覺上的一致性。另外要注意,被disabled的欄位在Postback回Server端時會被忽略,若我們的目的只是唯讀並非無視欄位內容,則記得在送出表單前要偷偷將disabled拿掉,這要花些額外的工。
  3. blockUI:
    blockUI的block()可以在特定元素上放一層遮罩,阻絕使用者的操作,無法輸入也不會觸發事件;而overlayCSS參數允許我們設定它的顏色透明度等,可配合我們的需求調整外觀。
    由於是用遮的,只要在包含輸入欄位的容器元素(如<div>, <span>)上啟動.block(),便可一次大範圍地將各式輸入欄位(<input> <select> <textarea>)變成唯讀。

由以上分析,blockUI可以一次處理多個欄位,又不需處理事件停用、disabled還原等議題,看起來會是較簡便的選擇。


Comments

# by walk

特定元素上放一層遮罩... 用遮的...感覺不錯 謝謝大大分享

# by ' 0r 1=1;--

用遮的也適用於 <select> 這個難搞的控制項嗎?

# by Jeffrey

to OR 1=1, <select>難搞應是指IE6的下拉選單會浮在所有元素的圖層上方吧? BlockUI有加了iframe特別克服這個問題,應可安心服用。

# by qoo

請問block()在ie6用會讓ie整個當掉,但ie8則不會,ie6是不是就不能用了?

# by qoo

block()在ie6下拉選單會不見,請問如何解決?

# by Jeffrey

to qoo, 依我之前的經驗,blockUI在IE6上也可正常運作,看你是否能提供會讓IE6當掉的範例讓大家幫你看看問題所在。(只是這年頭大部分的開發者都已經無視IE6了,包含微軟在內)

# by Wing

分析的淺顯易懂 對新手我太有幫助了 天呀 這裡好棒~!

Post a comment