關於JS更新input元素的value屬性引發的狗血討論

快應用研發中心發表於2019-04-17
來個面試題:
  1. 在瀏覽器中,對於input節點的使用者輸入,何時觸發"change"事件呢?
  2. 在瀏覽器中,JS更新input的value屬性,會觸發"change"事件嗎?
  3. 如果不會觸發,請問為什麼?有什麼辦法解決嗎?
根據對上面問題的理解,我分為以下三個認知階段:


第一階段:讀者只需要答案即可:
  1. 當input在使用者輸入後,節點失去焦點的時候觸發”change"事件,並且在“input"事件之後;
  2. 如果讀者沒理解,可以看下面的程式碼;結論是:不會觸發”change"事件
  3. 目前搜尋到的W3C規範中,貌似沒有這樣的能力,包括:"MutationObserver";當然,如果開發者呼叫dispatchEvent的方式不算;
關於JS更新input元素的value屬性引發的狗血討論
如果讀者只在乎結論的話,就可以不往下看了;下面聊下W3C的定義;

第二階段:W3C的規範約束:
大家是否好奇:JS更新input的value屬性,為什麼不觸發"change"事件呢?
所以,我們來看下W3C是如何定義"change"事件的,下面摘自連結中:
The onchange event occurs when a control loses the input focus and its value has been modified since gaining focus. This attribute applies to the following elements:INPUT,SELECT, andTEXTAREA.
規範說:該事件源於input控制元件在使用者操作後失去焦點並且value變化時觸發;
另外,我們再看看W3C是如何定義value更新的行為的,下面摘自連結中:
value On getting, return the current value of the element. On setting: 1. Let oldValue be the element's value. 2. Set the element's value to the new value. 3. Set the element's dirty value flag to true. 4. Invoke the value sanitization algorithm, if the element's type attribute's current state defines one. 5. If the element's value (after applying the value sanitization algorithm) is different from oldValue, and the element has a text entry cursor position, move the text entry cursor position to the end of the text control, unselecting any selected text and resetting the selection direction to "none".
可以看到:其中並沒有約定要觸發"change"事件;
所以各類瀏覽器不觸發事件是很正確的表現了;
如果讀者只在乎W3C定義的話,就可以不往下看了;下面聊聊為什麼;

第三階段:為什麼W3C這麼定義;
大家是否好奇:JS更新input的value屬性,W3C為什麼不觸發"change"事件呢?
有人說,W3C就是這麼定義的啊!
實際上,我想問的是:為什麼W3C這麼定義呢?
我覺得"change"事件這麼定義在語義上有問題,如果不想有問題,那麼辦法有兩種:
  1. JS更新,觸發”change"事件;
  2. 將規範中的"change"事件名稱,更名為:"userchange"或者"uachange";(ua代表user agent)
為什麼說語義有問題,解釋如下:
站在開發者的認知角度上,"change"代表的是一種"狀態"的變化,它不在乎是"主動改變"還是"被動改變";主動為使用者的輸入改變,被動為程式碼層面的更新賦值;
按照正常人對上面"change"單詞的語義理解,JS更新value,也屬於一種狀態change(更何況UI都會變化),就應該觸發"change"事件才對;
帶著這個疑問我問了一圈身邊的開發者,有了下面的對話:
作者:問個問題:通過JS修改input的value值,為什麼監聽的input,change事件不會觸發呢?為什麼這麼設計? 

大壯:我理解的change事件應是在value只改變且失去焦點時才會觸發,應該是從效能上考慮的吧 

翠花:value的改變是事件觸發的結果,如果變成原因是不是就死迴圈了 二壯:應該是避免事件冒泡導致不必要的效能損失吧,我猜的

 W3C:When the change event applies, if the element does not have an activation behavior defined but uses a user interface that involves an explicit commit action, then any time the user commits a change to the element's value or list of selected files, the user agent must queue a task to fire a simple event that bubbles named change at the input element, then broadcast formchange events at the input element's form owner. 

作者:Chrome原始碼沒看到線索,查了W3C,我理解這麼個意思: change事件屬於使用者GUI操作產生的,對於通過JS改變的行為,不屬於這類管理範圍; 作者:我深深覺得這是web的坑,很早這種行為就定了,又沒法相容。你看Android就不是這種行為呀。從名字上來說,input事件不觸發合理,change事件名,從直觀理解就應該觸發呀。js更新不算change?

 對話:... 後面又聊到了"event loop"與非同步機制,對話走偏了,就不扯了;

事實上,如果在Android平臺中,通過程式碼修改,確實會觸發類似的"textchanged"事件的;
由於兩個平臺表現不一致,沒辦法,在Github帶著疑問去W3C問了;


老外效率真心高,第二天就回復了;
老外2:Unfortunately, we cannot change the names of events that have been dispatching for over two decades.
事實證明了我的看法,規範的原始定義造成了既成事實
如果硬要揪毛病,確實不合適;但是已經定義了20多年了,再改很容易不相容!

好了,截止這裡就結束了;

之所以問這個問題,是因為快應用在設計的時候,就面臨著:

到底遵守W3C規範(不觸發),還是Android其它平臺(觸發)的行為;

個人看法,應該觸發事件,不遵守規範;


相關文章