這不是一個真實的瀏覽器事件,不過的確曾經存在這樣一個事件滿足我們的需求。
很多時候,我們需要對文本域的值進行變化檢測,不論是這變化是用戶通過鍵盤敲打引發的,或是通過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的輸入雙向綁定就輕松多了。