使用自訂確認對話框取代window.confirm
8 |
專案規格有一條機車要求: 對於刪除或覆寫資料前的確認程序,希望以自訂風格的確認對話框取代簡陋的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>
Comments
# by chihwen
黑大您好,也可考慮採用 jQuery BlockUI 套件看看。 http://www.malsup.com/jquery/block/
# by Jeffrey
to chihwen, 呵! blockUI已是我專案的固定班底,本次套件海選未能出線,是因為專案已使用Kendo UI,而kendoWindow提供現成的Window Title、控制位置的API,更符合需求。 但謝謝你的推薦,我會轉告blockUI它又多了一名粉絲(我也是blockUI後援會成員)。
# by 火影
請問黑大,觀察範例中,有個變數使用 var df,有的卻又 var $div ;不知這樣撰寫是為了甚麼?還是有其他不為人知的意義?
# by Jeffrey
to 火影,這算是我個人的撰寫慣例,用來存放jQuery物件的變數,我會加上"$"字首,方便後續快速分辦它是jQuery物件,就能大膽地套用.find(), .data()等方法。
# by Smalle
第一个实例拿下来运行,怎么不是同步的啊
# by Jeffrey
to Smalle, 請問不同步指的是什麼?能放上JSBin或JSFiddle給個演示嗎?
# by JerryH
有法子覆寫window.confirm 嗎?讓confirm 直接用kendoConfirm?
# by Jeffrey
to JerryH, 覆寫 confirm 很容易,但 kendoConfirm 是非同步的,後續程式碼要放在 done()/faile() 裡,無法 100% 模擬 confirm() 卡住程式等回應的行為。即使覆寫,也沒法做到程式完全不改。