[Javascript] IE11でRadioボタングループのvalueを取得できない事象&対策

概要

この記事について

Javascriptを書き、各ブラウザ対応をしたい(特にOldブラウザで動くようにしたい)場合、
Babelを導入・実行することで
トランスパイルにより構文や関数を汎用的なものに変換する方法がデファクトスタンダートかと認識している。

変換の処理は開発者の手を煩わすことなく短時間で済むのがBabelの強力さだが、
時折Babelの変換のスコープ外であるような機能があり、手動で対応策を探らなければならない。

IE11において、
FormのRadioボタングループを取得し、そのvalueプロパティを参照した際に、
想定した値が取得できなかったため、
事象と対策について記したい。

実行環境

  • ツール
    • @babel/core:7.14.6
    • @babel/preset-env:7.14.7
    • core-js:3.15.2
    • regenerator-runtime:0.13.9
  • ブラウザ
    • chrome:92.0.4515.107
    • IE11:20H2(OS Build 19042.1110)

事象

About

次のようなRadioボタンを含むシンプルなフォームがある。

 1<form id="my-form" name="my-form" onsubmit="return false;">
 2    <h3>県庁所在地の表示</h3>
 3    <div>
 4        <input id="radio01-toyama" type="radio" name="radio01" value="富山市">
 5        <label for="radio01-toyama">富山県</label><br>
 6        <input id="radio01-ishiakwa" type="radio" name="radio01" value="金沢市">
 7        <label for="radio01-ishiakwa">石川県</label><br>
 8        <input id="radio01-fukui" type="radio" name="radio01" value="福井市">
 9        <label for="radio01-fukui">福井県</label>
10
11    </div>
12    <div id="result"></div>
13    <div id="button-container">
14        <button id="button01">表示</button>
15
16    </div>
17</form>

次のようなJSコードをページで実行するとする。

1// ...
2// FormのRadioボタングループを取得し、valueを参照
3// = Radioボタンのチェックされたボタンのvalue属性を参照することと同等
4const radio01Value = document.forms["my-form"].radio01.value;
5// ...

Babelによるトランスパイル後、
ブラウザにおいて動作確認すると、
chrome他モダンブラウザでは想定通りの挙動だが、
IE11 ではどうもうまくいかない。

デモページ

こちらに デモページ があります。

対応ブラウザでは、選択したラジオに応じた県庁所在地が表示されます。

何が起こっているか?

form.radioGroupNameの呼び出しで得られる値の型の違い。
モダンブラウザではRadioNodeListであるが、IE11ではHTMLCollection となるようだ。

1// モダンブラウザ
2document.forms["my-form"].radio01 instanceof RadioNodeList === true
3// IE11
4document.forms["my-form"].radio01 instanceof HTMLCollection === true
5

ゆえに、
上記事象ではIE11ではHTMLCollection.prototype.valueという未定義プロパティを参照するため、想定外の挙動となる。

対応策

いくつかあるかと思う。

prototypeに対して挙動を定義しモダンブラウザに寄せる

ロードしたときの実行スクリプトのどこかで
次のように定義しておく。

1Object.defineProperty(HTMLCollection.prototype, "value", {
2    get: function () {
3        for (let idx = 0; idx < this.length; idx++) {
4            if (this[idx].checked) return this[idx].value;
5        }
6        return undefined;
7    },
8});
9

Pros(長所)

定義しておけば、
以降のRadioボタンまわりのコードを書き換える必要がない。

Cons(短所)

  • 今後のブラウザのアップデートで
    HTMLCollectionにvalueプロパティ(orアクセッサ)が定義された場合、
    衝突を起こして想定外の挙動となるかもしれない。
  • コメントで実装の意味を記しておかないと
    実装者以外が意図を測りかねるかもしれない。

CSSセレクターで要素を取得する

jQueryを用いるか、querySelectorAPIを用いて
CSSのセレクター構文で要素を指定して、チェックされたボタンのvalue属性を直接取得するようにする。

[jQuery]

1const radio01Value = $("form[name='my-form'] input[type='radio']:checked").val();

[プレーンJS]

1const radio01Value = document.querySelector("form[name='my-form'] input[type='radio']:checked").value;

Pros

シンプル。
変換前の書き方と同じように一行で書き下すことができる。

Cons

書き換えコストは少ないものの、
Radioボタンまわりのコードを書き換える必要がある。

まとめ

原因はコレクション参照時のオブジェクトの型の違い。
対応策はいくつかあるが、querySelectorで一行で置き換えるのが良いかと思う。

comments powered by Disqus