這不是一個真實的瀏覽器事件,不過的確曾經存在這樣一個事件滿足我們的需求。
很多時候,我們需要對文本域的值進行變化檢測,不論是這變化是用戶通過鍵盤敲打引發的,或是通過el.setAttribute("value","aaa")引發的,還是el.value = "bbb"引發的,還是用戶通過復制粘貼引發的,更惡心的是HTML通過語音輸入引發的。只要里面的文字發生變化,我們希望都能比較及時調用相應的程序進行處理。
在舊式IE(IE6-8)下,onpropertychange可以滿足上述所有需求(由於IE不支持input[speed]),因此不用考慮最后一種輸入)。
IE9開始區分attribute和property了,從許多場合來是好事,但在這場合則是壞事,它相當於DOM3變動事件中的DOMAttrModified。只對用戶輸入與setAttribute見效,不支持直接賦值方式的檢測。
標准瀏覽器也提供了一個input,但它也不支持直接賦值方式的檢測。即便最新式的mutationObserver API,對el.value ="zzz"這種屬性賦值法也無濟於事。因此自己動手富衣足食。
取一個文本域的值最簡單不過,對兩個字符串進行比較也最簡單不過。我們要做的是得到先后兩個值。因此需要用最通用的事件進行冒允。當我們在PC上輸入內容,肯定會觸發mousedown事件,也肯定觸發mousedown。進行輸入時確定會觸發鍵盤事件,根據我的知識庫,keypress會對一些系統鍵失靈,因此最好用keyup,keydown。但用戶不一定通過鍵盤輸入,HTML5還提供了語音輸入,只要點了那個麥風筒圖案就可以口述了,麥風筒也在input上,因此mousedown肯定也有。當我們輸入后肯定會繼續其他表單元素的填寫或進行提交,因此會觸發blur事件。從mousedown到blur這段時間,我們可以通過定時器加keydown, keyup, webkitspeechchange的回調進行值變化判定,變化了就執行用戶回調,把前后兩個值放到事件對象中。
下面是mass Framework的實現:
define("valuechange", ["$event"], function(){
var DATA = "valuechangeData";
var ID = "valuechangeID"
var interval = 50;
//如果值前后發生改變,觸發綁定回調
function testChange(elem, type, poll) {
if(poll){
$._data(elem, ID, setTimeout(function(){
testChange(elem, type, poll);
},interval));
}
var old = $._data(elem, DATA);
var neo = elem.value;
if(old !== neo){
$._data(elem, DATA, neo);
var event = new $.Event("valuechange")
event.oldType = type
event.oldValue = old;
event.newValue = neo;
$.event.fire.call(elem, event)
}
}
function unTestChange(elem){
var id = $._removeData(elem, ID)
clearTimeout( id )
$.log($._removeData)
$._removeData(elem, DATA);
}
function startTest(event) {
var elem = event.target;
if (event.type == 'focus' ) {
$._data(elem, DATA , elem.value);
}
testChange(elem,event.type, true);
}
function stopTest(event){
unTestChange(event.target)
}
function listen(elem) {
unlisten(elem);
"keydown keyup mousedown focus".replace($.rword, function(name){
$(elem).bind(name+"._valuechange", startTest)
})
$(elem).bind('blur._valuechange', stopTest);
//http://liumiao.me/html/wd/W3C/264.html
$(elem).bind('webkitspeechchange._valuechange', function(e){
testChange(e.target,e.type)
});
}
function unlisten(elem){
unTestChange(elem)
$(elem).unbind("._valuechange")
}
$.fn.valuechange = function(callback){
return callback? this.bind( "valuechange", callback ) : this.fire( "valuechange" );
}
$.eventAdapter.valuechange = {
setup: function(desc){
var elem = desc.currentTarget, nodeName = elem.tagName;
if (nodeName == 'INPUT' || nodeName == 'TEXTAREA') {
listen(elem);
return false
}
},
tearDown: function (desc) {
unlisten(desc.currentTarge);
return false
}
}
})
它的事件系統的架構與jQuery的很相近,都是通過setup, teardown方法來綁定特殊的事件。我們只對INPUT元素及文本區進行操作,一定進入分支,就調用listen監聽函數。接下來綁定的事件的作用依次是:
- mousedown:監聽粘貼。
- keyup,keydown:監聽鍵盤輸入。
- focus:監聽一切引發聚焦的事件,比如在觸摸屏下。
- webkitspeechchange:監聽語音輸入。
- blur:清理定時器,比如雖然已經放在文本域中啊,但長期沒有動,那么只有等keyup再次觸發新的定時器了。
具體例子可見這里。
有了這個onvaluechange事件,以后做表單驗證或自動完成及MVVM的輸入雙向綁定就輕松多了。
