Angular + IE9 + select タグ でプレースホルダー がちゃんと出なかった

開発 hironemuhironemu

Angular 1.2.23 でプルダウン(select)を生成するという簡単なプログラムを書いていた時、IE9で思ったように表示してくれない現象に出会ってしまって大ハマリしたので、メモっておきます・・・。

まず、Angular でHTMLを出力するためにテンプレートの機能を使っていました。そして、以下の様なテンプレートのHTMLを作成しました。ng-options でリストを生成しています。 value が空文字の option タグを用意しておくと1行目にプレースホルダーとしての option を作成してくれます。便利ですね。

<select ng-model="a" ng-options="x for x in [1,2,3,4,5]">
  <option value="">-- Select One --</option>
</select>

これをIE9で実行したところ、なぜか以下の1つ目のプルダウンのようにプレースホルダーが消えてしまっています。(2つ目は問題を解決したあとのコードが表示したものです。どちらもリストの中身はちゃんと出ています)

原因がどこにあるかわからず一つ一つ問題を潰していくと、最終的にテンプレートのHTMLを圧縮したあと生成されるHTMLに問題があることが分かりました。

本来、以下のように生成されて欲しかったHTMLが、

<option value="">-- Select One --</option>

以下のように、value のあとの ="" が消された状態で出力されていました。

<option value>-- Select One --</option>

今回使用していたgulpのプラグインのgulp-minify-htmlが利用するminimizeというライブラリのオプションを以下のようにすることでこの問題は解決しました。(もともと、以下のオプションの spare 以外は指定していたので、上記の様なアウトプットになっていました。)

minifyHTML({
  quotes: true,
  empty: true,
  spare: true
})

では、なぜ value="" でないとIE9でちゃんと動作しないかというと、angular.js(1.2.23)の以下の場所で最初に option が空のものを探してそれを emptyOption 変数に代入するという処理をやっています。

21502
21503
21504
21505
21506
21507
21508
// find "null" option
      for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
        if (children[i].value === '') {
          emptyOption = nullOption = children.eq(i);
          break;
        }
      }

ここで childen[i].value とやっているところがあります。

これは、DOM の HTMLOptionElementvalue 値を取得しているのですが、IE9では <option value> となっている場合、value の値ではなく option タグのテキストの方を取得してくるようになっているようです(今回の例だと – Select One – という文字列)。なので、上記21504行目の処理はスルーされてしまい、せっかく定義したプレースホルダーのoptionが無かったことにされる、というわけでした。

ちなみに、 value 属性がない(空文字でなく、属性そのものがない)場合というのは、上記と同じような仕様(option タグの中のテキストを返す)になるっぽいですね。

https://developer.mozilla.org/ja/docs/Web/API/HTMLOptionElement

テンプレートのHTMLがminifyされて生成されたHTMLをちゃんと意識してないのが大ハマリした原因でした。

Tags: