使用自訂確認對話框取代window.confirm

專案規格有一條機車要求: 對於刪除或覆寫資料前的確認程序,希望以自訂風格的確認對話框取代簡陋的window.confirm()。

舉例來說,按鈕後原本要透過window.confirm()請使用者確認後再執行,現在要改用自訂HTML元素呈現確認文字、按鈕進行確認,就如以下改用Kendo UI Window實作確認對話框的效果:

用HTML打造自訂對話框並在適當時機顯示是小事一椿,較有挑戰性的部分是原本window.confirm()執行為同步式,程式碼會停住等使用者回應再繼續往下走。想依confirm()結果決定不同動作只需寫成:

if (window.confirm("確定嗎?")) {
    //…使用者回答【是】時的動作
} else {
    //…使用者回答【否】時的動作
}

但要在JavaScript做到"卡住流程直到特定條件再繼續"並非易事,曾看過一種做法是跑while無窮迴圈並於特定時機跳出,但因JavaScript不像C#有Thread.Sleep可用,無窮迴圈會莫名吃光CPU很不環保。也有人想出藉由同步式XHR到Server端存取虛設延遲網頁模擬Thread.Sleep的招術,但靠著無謂網路傳輸來節省CPU,還徒增Server負擔,想來也不怎麼高明。最後,還是決定用jQuery的Deferred來處理非同步。

原理是先宣告Deferred物件,當呼叫確認對話框時,傳回Deferred.promise()給呼叫端,呼叫端可透過.done()指定使用者按【是】時要執行的動作、在.fail()指定使用者按【否】時要執行的動作。如此,再依使用者按鈕決定呼叫Deferred.resolve()或Deferred.reject(),就能控制該觸發done()還是fail(),達到依操作結果決定不同執行動作的效果。先不管華麗的UI元素,以下是示範用Deferred依按鈕結果決定動作的簡單範例: 線上展示

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
<meta charset=utf-8 />
<title>使用Deferred建立自訂確認對話框</title>
  <script>
    function myConfirm(msg) {
      var df = $.Deferred(); //建立Deferred物件
      var $div = $("<div id='C'></div>");
      //由樣版複製建立一次性div元素
      $div.html($(".dialog").html())
      //加上按鈕事件
      .on("click", "input", function() {
        $div.remove(); //將對話框移除
        if (this.value == "Yes") 
          df.resolve(); //使用者按下Yes
        else 
          df.reject(); //使用者按下No
      })
      .find(".m").text(msg); //設定顯示訊息
      //將div加入網頁
      $div.appendTo("body");
      return df.promise();
    }
    
    $(function() {
      $("#btnTest").click(function() {
        myConfirm("Are you sure?")
        .done(function() { //按下Yes時
          alert("You are sure");
        })
        .fail(function() { //按下No時
          alert("You are not sure");
        });
      });
    });
  </script>
</head>
<body>
  <input type='button' value='Test' id='btnTest' />
  <div class='dialog' style='display:none'>
    <div style='border: 1px solid blue; padding: 12px;'>
    <span class='m'></span>
    <input type='button' value='Yes' />
    <input type='button' value='No' />
    </div>
  </div>  
</body>
</html>

其操作結果如下:

最後,運用同樣原理再招喚Kendo UI的Window套件上場,就能實現一開始展示的華麗版確認對話框囉~ 完整程式碼如下: 線上展示

<!DOCTYPE html>
<html>
<head>
  <title>使用Deferred建立自訂確認對話框(Kendo UI版)</title>
<link href="http://cdn.kendostatic.com/2013.2.716/styles/kendo.common.min.css" 
 rel="stylesheet" type="text/css" />
<link href="http://cdn.kendostatic.com/2013.2.716/styles/kendo.default.min.css" 
 rel="stylesheet" type="text/css" />
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
<script src="http://cdn.kendostatic.com/2013.2.716/js/kendo.web.min.js"></script>
<meta charset=utf-8 />
  
  <style>
    .cnfrm-msg { color: red; padding: 12px; font-size: 12pt; }
    .cnfrm-yes,.cnfrm-no { }
  </style>
  <script>
    //參考: http://jsfiddle.net/gyoshev/HRcKK/
    (function($) {
      var h = [];
      h.push("<div class='cnfrm-block'>");
      h.push("<div class='cnfrm-msg'></div>");
      h.push("<input type='button' class='cnfrm-yes' />");
      h.push("<input type='button' class='cnfrm-no' />");
      h.push("</div>");
      var html = h.join("");
      
      $.kendoConfirm = function(title, msg, yesText, noText) {
        var $div = $(html);
        $div.find(".cnfrm-msg").text(msg);
        $div.find(".cnfrm-yes").val(yesText || "Yes");
        $div.find(".cnfrm-no").val(noText || "No");
        var win = $div.kendoWindow({
          title: title || "Confirmation",
          resizable: false,
          modal: true,
          deactivate: function() {
            this.destroy(); //remove itself after close
          }
        }).data("kendoWindow");
        win.center().open();
        var dfd = $.Deferred();
        $div.find(":button").click(function() {
          win.close();
          if (this.className == "cnfrm-yes")
            dfd.resolve();
          else
            dfd.reject();          
        });
        return dfd.promise();
      };
    })(jQuery);
    
    $(function() {
      $("#btnTest").click(function() {
      var dfd = 
        $.kendoConfirm(
          "Please confirm...",
          "Are you sure to delete it?",
          "Yeeees", "No no no");
        dfd.done(function() { //按下Yes時
          alert("You are sure");
        })
        .fail(function() { //按下No時
          alert("You are not sure");
        });
      });
    });
  </script>
</head>
<body>
  <input type='button' value='Test' id='btnTest' />
  <div class='dialog' style='display:none'>
    <div style='border: 1px solid blue; padding: 12px;'>
    <span class='m'></span>
    <input type='button' value='Yes' />
    <input type='button' value='No' />
    </div>
  </div>  
</body>
</html>
歡迎推文分享:
Published 03 August 2013 10:36 AM 由 Jeffrey
Filed under: ,
Views: 27,307



意見

# chihwen said on 03 August, 2013 12:47 AM

黑大您好,也可考慮採用 jQuery BlockUI 套件看看。

www.malsup.com/.../block

# Jeffrey said on 05 August, 2013 03:29 AM

to chihwen, 呵! blockUI已是我專案的固定班底,本次套件海選未能出線,是因為專案已使用Kendo UI,而kendoWindow提供現成的Window Title、控制位置的API,更符合需求。

但謝謝你的推薦,我會轉告blockUI它又多了一名粉絲(我也是blockUI後援會成員)。

# 火影 said on 22 October, 2013 11:57 PM

請問黑大,觀察範例中,有個變數使用 var df,有的卻又 var $div ;不知這樣撰寫是為了甚麼?還是有其他不為人知的意義?

# Jeffrey said on 23 October, 2013 12:22 AM

to 火影,這算是我個人的撰寫慣例,用來存放jQuery物件的變數,我會加上"$"字首,方便後續快速分辦它是jQuery物件,就能大膽地套用.find(), .data()等方法。

# Smalle said on 04 July, 2016 05:35 AM

第一个实例拿下来运行,怎么不是同步的啊

# Jeffrey said on 04 July, 2016 11:38 PM

to Smalle, 請問不同步指的是什麼?能放上JSBin或JSFiddle給個演示嗎?

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<August 2013>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication