input type=rangeとchangeイベントまわりで遊んでみたらいろいろバグっぽい挙動に出くわした

たまには、みんな大好きJavaScriptなエントリーでも書いてみようかなって。
まず、そもそもJSの書き方がわかんないので適当にぐぐってみたらこういうコードが出てきた。

<input id="range01" type="range" min="0" max="100" step="2" onchange="showValue()"/>
<span id="showRangeArea">0</span>
 <script type="text/javascript">
  function showValue () {
     document.getElementById("showRangeArea").innerHTML = document.getElementById("range01").value;
  }
</script> 
[HTML5] HTML5の新しいインターフェース、スライダーを試してみた - YoheiM技術やらずに終われまテン

type属性いらない、innerHTML使うべからず、イベントハンドラコンテンツ属性よりイベントリスナーっしょ、みたいな声が聞こえてきそうなので、ついでに少し盛って書き直してみた。

<p>
<label for="range01">範囲が0~100、ステップが0.2なスライダーに50ごとに目盛りをつける</label><br />
<input id="range01" type="range" min="0" max="100" step="0.2" value="0" list="scale" />
<span id="showValueArea">0</span>
</p>
<script>
  var obj = document.getElementById("range01");
  function showValue() {
    document.getElementById("showValueArea").textContent = obj.value;
  }
  obj.addEventListener("change", showValue, false);
</script>
<datalist id="scale">
<option>0</option>
<option>50</option>
<option>100</option>
</datalist>

まあ、やってることはそんなに高尚なことではなく、単にスライダーの指示値をinput要素の横に表示するというだけのごく簡単なもの。のはずなのだけど、なぜかバグっぽい差異がいろいろと見受けられる。


1) Firefoxdatalistが機能しない
たとえばBlink (Vivaldi)でこういう感じで目盛りが表示されるのだけれども、Firefoxでは表示されない(HTML仕様で目盛りの表示は示唆されている*1


2) IE/Edgeで、キーボードで右(または左)に連続してスライドさせていくと、数値がstepに従わなくなる
たとえば0からはじめて右にずっと移動させていくと、数値が下記のようになる。*2


3) マウスでつまみをつかんだままスライドするとき、IEのみリアルタイムで数値が更新される
FirefoxとBlinkでは数値は変化しない(つまみを離したときに数値が更新される)


4) キーボードでつまみをスライドさせても、Firefoxではspan要素の値が更新されない
どうも、Firefoxではイベントが発火しないみたい。(フォーカスが外れないと発火しない)

5) inputbackgroundプロパティー等のスタイル設定をすると、Firefoxでつまみが変形する
たとえば、

<input type="range" style="background-color:yellow;" />

とかすると、次の図のようになる*3


1)と2)はバグの疑いが濃厚だけれども、3)と4)はどうなのか。仕様のchangeイベントを当たってみよう。

When the input and change events apply (which is the case for all input controls other than buttons and those with the type attribute in the Hidden state), the events are fired to indicate that the user has interacted with the control. The input event fires whenever the user has modified the data of the control. The change event fires when the value is committed, if that makes sense for the control, or else when the control loses focus. In all cases, the input event comes before the corresponding change event (if any).

WHATWG HTML 4.10.5.5 Common event behaviours

仮訳:(ボタン以外のすべてのinputコントロールおよび非表示状態でtype属性をもつ場合に)inputおよびchangeが適用される際、イベントは、ユーザーがコントロールと相互作用したことを示すために発火される。inputイベントはユーザーがコントロールのデータを変更するたびに発火する。changeイベントがコントロールに対して理にかなう場合、値がコミットされた際に、あるいはそのコントロールがフォーカスを失った際に、そのイベントは発火する。すべての場合において、inputイベントは、対応するchangeイベントの前に来る(もしあれば)。*4

ということらしい。まあ、実装の差異と取るべきなのかというのがいまいち判断付かないけども。


ここで、changeイベントの代わりに「データを変更するたびにいつでも発火する」というinputイベントとすると、IEでは思い通りに動かない(Edgeはちゃんと動く)。また、Firefoxでマウスとキーボードの挙動の違いはなくなった。changeイベントが鬼門と言うことなのか……というか素人目になんでもかんでも発火してそうなinputイベントを使うのは妥当なのだろうか、まるでこのあたり分かってない。

ついでに、MDNのinputイベントの項目にはDOM Level 3 Events改めUI Events仕様にinputイベントが定義されているとあるけれども、見つけることができなかった。さらに、UI Events仕様にあるinputを踏むと、Edit Events仕様に飛ぶ場合があるけれども、Edit Events仕様でもやっぱり定義されていない。一体どうなっているの……。*5

確かめてみた環境とか

Windows 10 x86-64上のIE11、Edge、Firefox42 (Stable)、Vivaldi Beta (Chrome/45.0.2454.99)。

*1:一応バグレポ投げてみた https://bugzil.la/1222762

*2:類似のバグレポがすでに投げられているっぽい https://connect.microsoft.com/IE/Feedback/Details/1590251

*3:https://bugzil.la/979455 に類似のバグレポが投げられていたのでコメントを付けたけど、これで良かったのだろうか、、。

*4:しかし、なぜこの部分がW3C HTML5のdeveloper-viewの対象外なのか、いまいち理解に苦しむ

*5:https://twitter.com/d_toybox/status/664072780044828672 ということらしい。