Element.classListをInternet Explorerで使うときの注意

IEだとうまく動かないJavaScriptがあり、調べたところ Element.classList が原因だった。

まだまだIE11は切れない環境なので、備忘としてメモ。

環境

Windows 10 Pro 64bit、Internet Explorer 11.476.18362.0で確認。

IEでは動かないメソッド

MDNを見れば対応表がある。

developer.mozilla.org

今回引っかかったのは以下2つ。

add, removeの引数に配列を渡せない

classList.add(String [, String [, ...]]) および classList.remove(String [, String [, ...]]) が、他ブラウザでは文字列の配列として複数のクラス名を渡せるのに対し、IEでは文字列として単一のクラス名しか渡せない。

対応としては、IE9以降なら Array.forEach は使えるので、1つずつ渡してやればいい。

const targetElement = ...;
const targetClassList = targetElement.classList;
const targetClasses = ['foo', 'bar'];

// IE以外
targetClassList.add(targetClasses);
console.log(targetClassList); // foo,bar

targetClassList.remove(targetClasses);
console.log(targetClassList); // 空

// IE対応
targetClasses.forEach(function(targetClass) {
  targetClassList.add(targetClass);
});
console.log(targetClassList); // foo,bar

targetClasses.forEach(function(targetClass) {
  targetClassList.remove(targetClass);
});
console.log(targetClassList); // 空

こちらはエラーになってくれるのでわかりやすい。

toggleの第2引数を渡せない

こちらのほうがハマってしまった。

classList.toggle(String [, force]) は、第1引数の文字列のクラス名は必須だが、第2引数の有無で挙動が変わる。

  • 第2引数を渡さなければ、第1引数のクラスが指定されていれば削除、指定されていなければ追加
  • 第2引数を渡すと、その値がtrueと評価されれば classList.add と同様にクラスを追加、falseと評価されれば classList.remove と同様にクラスを削除

だが、IEの場合、第2引数が無視され、常に第2引数を渡さない場合の挙動となる。

対応としては、第2引数として渡していた値のtrue/falseで、それぞれadd/removeを呼ぶようにすればいい。

const targetElement = ...;
const targetClassList = targetElement.classList;
const toggleClass = 'foobar';

// IE以外
targetClassList.add(toggleClass);

targetClassList.toggle(toggleClass, !targetClassList.contains(toggleClass));
console.log(targetClassList); // 空

targetClassList.toggle(toggleClass, !targetClassList.contains(toggleClass));
console.log(targetClassList); // foobar

// IE対応
targetClassList.add(toggleClass);

if (!targetClassList.contains(toggleClass)) {
  targetClassList.add(toggleClass);
}
else {
  targetClassList.remove(toggleClass);
}
console.log(targetClassList); // 空

// ワンライナーで
targetClassList[!targetClassList.contains(toggleClass) ? 'add' : 'remove'](toggleClass);
console.log(targetClassList); // foobar

こちらのほうが、エラーにならないので面倒。

replaceが未実装

これは引っかかったわけではないが、いちおう書いておく。

classList.replace(String, String) で、第1引数のクラス名を第2引数のクラス名に置換できるが、IEではこのメソッド自体が未実装の模様。

対応としては、第1引数のクラス名を classList.contains で設定されているか判定、設定されていればremoveおよび第2引数のクラス名をaddしてやればよさそう。

valueプロパティが存在しない

IEではclassList関連で使えない機能が色々ある。 - 青いやつの進捗日記。にて言及いただいていたので、2020/11/29追記。

ただ、MDN見てもcaniuse見てもclassList.valueがどうなのか、記述が無い…

については、classListはDOMTokenListインターフェースを実装しており、valueプロパティはDOMTokenList.valueとしてDOMTokenListで定義されている。そしてそれがIEでは未実装だった。

記事では String(element.classList) 文字列に変換しているが、classを文字列として取得したいのであれば、昔ながらのElement.classNameを使うのが手っ取り早そう。

確認したところ、 classList.valueclassName と同様、不要な半角スペースなど含んで返してくる。

昔は className を半角スペースで分割して、空文字は除外してどうのこうのとかやってたなぁ...

振り返り

IE6~8対応とかやっていたころに比べると、IE11ならだいぶ楽に対応できるようになったが、まだまだ非互換は残っているんだなぁ...

EdgeもChromiumベースになることだし、いっそ今のEdgeをIEとして更新してくれたらありがたい。