今天做了一個特殊的 input 欄位,其實目標只是做成類似像輸入信用卡號那樣,輸入1234
完,準備要輸入5
的時候,會在4
後面補上一個-
,變成1234-5
,不過我預期做的完美一點,所以考慮了很多狀況,例如:
- 複製貼上沒有
-
的資料後會自動格式化 - 已經輸入一部分資料後,游標移到前面插入資料也會正確格式化
- 直接用
DEL
或Backspace
來刪除資料,要讓使用者感覺不到-
- 先選取一些字元然後用
DEL
或Backspace
甚至是剪下來刪除資料後會重新格式化 - 以上幾種操作都不會讓游標亂跳
簡單看過目前一些信用卡相關的 library,在卡號輸入的部分是沒有全部達到的,要達成這些目標,幾乎是等於每個使用者的操作都要攔截下來,然後要抓到當欄位內的值,會用到的事件包括了 keyup、keydown、paste 和 input,等,其中本來我對於一般使用者敲打鍵盤輸入的事件是用 keyup,keyup 事件後會判斷游標位置和輸入的內容,如果需要的話就加上-
,然後調整游標位置,通常是 +1,弄好後測試一陣,發現如果按鍵輸入很快的話,游標位置會亂掉,應該要 +1 的卻錯過了,深入除錯一陣子之後發現,keyup 事件其實和欄位內的 value 變更是非同步的,所以不能確保 keyup 事件拿到的欄位值是正確的,能確保欄位值正確的,其實是 input 事件,不過 input 事件沒有 keyCode,所以只能自己判斷輸入了什麼,另外刪除內容時也不會觸發 input,還好DEL
和Backspace
是用 keydown 事件來處理,兩邊剛好錯開了。
雖然 input 事件似乎很好用,不過其實它在早期的時候支援度是不太好的,算是比較新的事件,有類似狀況的還有一個是 change 事件,我的印象中是某些瀏覽器的行為會不太正確,所以其實我一直都還不太使用,至於 input 事件,我則是需要在不支援的瀏覽器中 fallback 到 keyup 事件,所以就會需要偵測,找了一下在 Modernizr 有支援,仔細看一下內容其實可以發現不是很好偵測,然後我也不是很喜歡 Modernizr 的介面,所以目前用的是在 Modernizr Issue 210 裡面 AndyE 提供的版本,稍微精簡一些:
var inputSupport = "oninput" in document.body || checkEvent(document.body);
/*
The following function tests an element for oninput support in Firefox. Many thanks to
http://blog.danielfriesen.name/2010/02/16/html5-browser-maze-oninput-support/
*/
function checkEvent(el) {
// First check, for if Firefox fixes its issue with el.oninput = function
el.setAttribute("oninput", "return");
if (typeof el.oninput == "function")
return true;
// Second check, because Firefox doesn't map oninput attribute to oninput property
try {
var e = document.createEvent("KeyboardEvent"),
ok = false,
tester = function(e) {
ok = true;
e.preventDefault();
e.stopPropagation();
}
e.initKeyEvent("keypress", true, true, window, false, false, false, false, 0, "e".charCodeAt(0));
document.body.appendChild(el);
el.addEventListener("input", tester, false);
el.focus();
el.dispatchEvent(e);
el.removeEventListener("input", tester, false);
document.body.removeChild(el);
return ok;
} catch(e) {}
}
測試困難的主因是 Firefox 4 有 bug,所以需要真的建立一個 input 元件,然後用完整模擬 input 事件。然後雖然這個版本的比較精簡好懂,不過之後還是會因為 license 的關係改用 Modernizr 的版本吧。至於我做的 input field 呢,現在當然還是公司資產,大概要等我有空在假日重寫一個 Credit Card 的版本才會放出來吧。