專案規格有一條機車要求: 對於刪除或覆寫資料前的確認程序,希望以自訂風格的確認對話框取代簡陋的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依按鈕結果決定動作的簡單範例: 線上展示
<!DOCTYPEhtml>
<html>
<head>
<scriptsrc="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>
<inputtype='button'value='Test'id='btnTest'/>
<divclass='dialog'style='display:none'>
<divstyle='border: 1px solid blue; padding: 12px;'>
<spanclass='m'></span>
<inputtype='button'value='Yes'/>
<inputtype='button'value='No'/>
</div>
</div>
</body>
</html>
其操作結果如下:
最後,運用同樣原理再招喚Kendo UI的Window套件上場,就能實現一開始展示的華麗版確認對話框囉~ 完整程式碼如下: 線上展示
<!DOCTYPEhtml>
<html>
<head>
<title>使用Deferred建立自訂確認對話框(Kendo UI版)</title>
<linkhref="http://cdn.kendostatic.com/2013.2.716/styles/kendo.common.min.css"
rel="stylesheet"type="text/css"/>
<linkhref="http://cdn.kendostatic.com/2013.2.716/styles/kendo.default.min.css"
rel="stylesheet"type="text/css"/>
<scriptsrc="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>
<inputtype='button'value='Test'id='btnTest'/>
<divclass='dialog'style='display:none'>
<divstyle='border: 1px solid blue; padding: 12px;'>
<spanclass='m'></span>
<inputtype='button'value='Yes'/>
<inputtype='button'value='No'/>
</div>
</div>
</body>
</html>