在網頁設計中輸入欄位連動是很常見的情境,例如有員工編號及員工姓名兩個欄位,當使用者在輸入員工編號後,系統需自動帶出員工姓名。一般最直覺做法是利用<input>的onchange或onblur事件,在使用者輸入完成後送出AJAX呼叫向伺服器查詢後設定姓名欄位。程式範例如下: (展示)
<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>傳統OnChange觸發</title>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
<script>
$(function () {
$("#txtEmpNo").change(function () {
$.get(
"DataSrc.ashx",
{ key: $(this).val() },
function (res) {
$("#txtEmpName").val(res);
});
});
});
</script>
<style>
.fld {
width: 80px;
}
</style>
</head>
<body>
員編:
<inputid="txtEmpNo"class="fld"/>
<inputid="txtEmpName"readonlyclass="fld"style="background: #CCC"/>
<inputtype="button"value="查詢"/>
</body>
</html>
預期的操作流程是: 使用者輸入員工編號,右方灰底唯讀欄位帶出員工姓名供確認員編無誤,接著再按下查詢。
但是因為帶出姓名的邏輯放在change事件,使用者輸入124後,必須按Tab或點選其他網頁元素才會觸發AJAX呼叫。不熟操作的使用者,可能會在輸入完124後停止動作,卻苦等不到姓名出現,摸索一段時間才學到"多按一下Tab"的撇步。
為了改善操作流暢度,我們試著將查詢姓名的邏輯由onchange事件移到onkeyup按鍵事件,如此使用者按完鍵,毋需按Tab或點選其他欄位,系統就會直接帶出姓名。這種設計,應用於編號長度固定的情境還算完美,因為在keyup事件中可以檢查已輸入的文字長度,等待編碼完整再送出查詢。但若有效資料長度不一,就會產生困擾。例如: 在我們的範例程式中,1代表Transparent、11代表Blue、114代表Purple,於是在輸入114的過程,左側的欄位會依序冒出Transparent、Blue及Purple(見下方動畫展示),這意味著程式觸發了無效查詢,也干擾了使用者操作。(展示)
開發世界高手如雲,便有人想出巧妙解法 -- 使用者在輸入過程一般會先有連續打字的階段,輸入完畢後則會出現停頓,因此程式便可依按鍵間隔偵測使用者是否已輸入完畢,待全部輸入完畢後再送出查詢。透過這個技巧,電腦瞬間通人性,不需要多按鍵,又知道等輸入完成再查詢,提供更好的使用者體驗。
要使用JavaScript實踐構想並不困難,只需要在每次keyup事件時,不要直接送出AJAX,而是以window.setTimeout預定一段時間再送出(例如: 1秒),而在設定setTimeout時要留下識別,以便下次keyup時,先以clearTimeout清除上次預約的AJAX動作,再設定新的預約AJAX動作。如此,若按鍵後未滿一秒又按鍵,前次的AJAX查詢就會被取消,直到打完字停頓超過一秒沒按任何鍵,AJAX查詢才會真的送出。這段邏輯寫起來就會像下面這樣:
$(function () {
var hnd;
$("#txtEmpNo").keyup(
function () {
//取消前一次預定的查詢
window.clearTimeout(hnd);
//延遲一秒後才查詢,若這一段內又輸入其他字元
//則此一預約執行會被上一行程式取消
var value = $(this).val();
hnd = window.setTimeout(function () {
$.get(
"DataSrc.ashx",
{ key: value },
function (res) {
$("#txtEmpName").val(res);
});
}, 1000);
});
});
操作過程如以下動畫所示,不需要額外Tab或點選,在輸入完成後又能自動帶出姓名,此種設計是不是更善解人意呢? (展示)
為了解說原理,我們剛才用setTimeout、clearTimeout自己造了輪子。但實務上不用這麼麻煩,網路已有現成Plugin可以實現前述構想,它有個術語叫Debounce(借用自電子學術語,指在接收訊息時避免發生誤動作的濾波機制),簡單列出幾個jQuery Plugin:
- jquery-debounce
- jQuery doTimeout (範例、說明較完整)
- jQuery-typing
以下是改用doTimeout Plugin後的寫法: (展示)
$(function () {
$("#txtEmpNo").keyup(
function () {
$(this).doTimeout("findEmpName",
1000, function () {
$.get(
"DataSrc.ashx",
{ key: $(this).val() },
function (res) {
$("#txtEmpName").val(res);
});
});
});
});
學會這招,大家不妨檢視自己專案中採用onchange、onblur觸發AJAX連動欄位的場合,看看有無調整的需要,試著讓自己的網頁作品更貼心吧!