初投稿です。
最近 Chrome にしか対応していないウェブサイトを改修する機会があった。適当に Babe って polyfi ったが、IE11 に完全対応するのは厳しそうで、それは諦めたが、 Edge44 は初期状態でちょいバグるくらいの感じだったので対応を試みた。URL の解決関係でハマったのでメモしておく。趣味人なので間違っていたらご教授ください。
URL コンストラクタの挙動が違う
JavaScript では new URL(url, [base])
の構文でベース URL を指定して URL オブジェクトを作ることができるが、url に空文字列を入れた場合の href の値が Chrome と Edge44 で微妙に違う。
const myURL = new URL("", "https://example.com/index.html")
// Chrome だと myURL.href === "https://example.com/index.html"
// Edge だと myURL.href === "https://example.com/"
const myURL = new URL("", "https://example.com/css")
// Chrome だと myURL.href === "https://example.com/css"
// Edge だと myURL.href === "https://example.com/"
const myURL = new URL("", "https://example.com/")
// base がスラッシュで終わる場合は(偶然)返り値が一致する
// ともに myURL.href === "https://example.com/"
const myURL = new URL("..", "https://example.com/index.html")
// url に空でない文字を入れると挙動は一致する
// ともに myURL.href === "https://example.com/"
const myURL = new URL("https://example.com/index.html")
// ともに myURL.href === "https://example.com/index.html"
const myURL = new URL("https://example.com/")
// ともに myURL.href === "https://example.com/"
「ベースURL」の意味からして、最初の例では Edge のように https://example.com/
を返してほしいところだが、Chrome ではそうはならない1。まるで空文字列を無視して(つまり base のつもりで入れたものを url として解釈して)いるようなふるまいを見せる。
new URL("", "https://example.com/index.html")
new URL("https://example.com/index.html")
// Chrome では同じ内容の URL オブジェクトができる
Chrome と空文字列の省略
そういえば経験的に、Chrome は空文字列を勝手に無いものと扱うような雰囲気がある。たとえば以下のように適当な DOM オブジェクトを作るとする。
const input = document.createElement("input");
input.setAttribute("type", "text");
input.setAttribute("value", data[i] || "");
input.required = flag===1 ? true : false;
データが存在しなければ value
の属性値に空文字列が入るわけだが、これを HTML に起こすとブラウザ間でけっこう差が出る。Chrome では =""
ごと削除され、属性名だけがタグ内に残るが、Edge44 (EdgeHTML) では空文字列の存在がきちんと残される。ちなみに Firefox は属性値省略絶対殺すマンになる。最近出た Edge79 (Chromium) では Chrome と同じように表示されるので、レンダリングエンジンの問題だろう。
<!-- Chrome, and Edge79 (Chromium) -->
<input type="text" value required>
<!-- IE11, Edge44 (EdgeHTML), and Firefox -->
<input type="text" value="" required="">
HTML の属性名・属性値を省略できるか問題はややこしい、これ以上の追及はやめておくが、ともかくこのような具合で Chrome では URL コンストラクタの第一変数が抹消されてしまい、挙動がバグったのだと思われる。本来 URL() コンストラクタの第一引数に空文字列が入ることは想定されていない? TypeError が出ても仕方がない場面だが、url も base もそれっぽい USVString なので一応動いてしまう。
ほかのオブジェクト型
気になったので開発者ツールのコンソールでいろいろ試してみたが、コンストラクタに空文字列を与えても無視されない。たとえば以下のペアは異なる結果が返ってくる。
new Date("", "2")
// Thu Mar 01 1900 00:00:00 GMT+0900 (日本標準時)
new Date("2")
// Thu Feb 01 2001 00:00:00 GMT+0900 (日本標準時)
new RegExp("", "g")
// /(?:)/g
new RegExp("g")
// /g/
なお Edge44 はまた違うふるまいをする。Chromium のお気持ち察し能力が高い。
new Date("2")
// [date] Invalid Date:
うーん、結局ほんとうの原因が何かはよくわからないままだ。WHATWG の仕様書とか読めば解決するのかもしれないけど、読み方わからないのでいったんここまで。
2020-02-05: Edge44 (EdgeHTML) をちょっと訂正。
-
つまりもともとの実装は、Chrome の不適切な挙動をもとに実装されていたということになる。 ↩