- バックエンド / リーダー候補
- PdM
- Webエンジニア(シニア)
- Other occupations (19)
- Development
- Business
location.assign(url) と location.href = url の違い
Photo by Julien Juanola on Unsplash
結論
普通は自ウインドウのlocationを操作するためhrefとassignは同じ挙動になりますが、自身とは異なるウインドウを操作する場合には挙動が異なります。具体的には実行中のコードと異なるオリジンを持つウインドウのlocationを操作しようとしたとき、
- location.href = url; ではオリジンの検査は行われず、操作は成功します。
- location.assign(url); ではDOMException (SecurityError) が発生します。
以下がそのデモです。iframe内に表示している https://example.com はこの文書自身とは異なるオリジンであるため、このようなiframeのlocationをassignで操作しようとするとエラーになります。
<!doctype html>
<html>
<head>
<script>
"use strict";
window.addEventListener("load", () => {
function go(win, url, type) {
switch (type) {
case "sethref":
win.location.href = url;
break;
case "assign":
win.location.assign(url);
break;
case "setlocation":
win.location = url;
break;
}
}
for (const type of ["sethref", "assign", "setlocation"]) {
const button = document.getElementById(type);
button.addEventListener("click", () => {
try {
go(window.frames[0], "https://www.w3.org/", type);
} catch (e) {
alert(e);
}
});
}
});
</script>
</head>
<body>
<iframe src="https://example.com"></iframe>
<button id="sethref">location.href = url</button>
<button id="assign">location.assign(url)</button>
<button id="setlocation">location = url</button>
</body>
</html>
また規定上は https://localhost:123456/
のような無効なURLを指定したときのエラーの種類も異なりますが、ChromeとFirefoxで確認したところどちらも規定通りには実装されていないようです。
どちらを使うべきか
上記のコーナーケースに当てはまらないのであれば、挙動の上ではどちらを選んでもよいでしょう。個人的にはメソッド形式である location.assign(url) のほうが副作用が適切に表現されているため好ましいのではないかと思います。
また以下のような亜種もあります。
- window.location = url; は location.href = url; と同様です。
- location.replace(url); は履歴管理の挙動が異なります。
仕様書の記述
location.href の挙動は以下のように定義されています。
The href setter steps are:
1. If this's relevant Document is null, then return.
2. Parse the given value relative to the entry settings object. If that failed, throw a TypeError exception.
3. Location-object navigate given the resulting URL record.
NOTE: The href setter intentionally has no security check.
ご丁寧にNOTEまで書いてあります。
一方、location.assign の挙動は以下のように定義されています。
1. If this's relevant Document is null, then return.
2. If this's relevant Document's origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
3. Parse url relative to the entry settings object. If that failed, throw a "SyntaxError" DOMException.
4. Location-object navigate given the resulting URL record.
この2番目の処理がassignのほうにだけ存在します。なお、hrefのgetterはassignと同様のチェックを行っています。 (クロスオリジンのウインドウやiframeのURLを読み取れないようになっている)
なお現時点では window.location = url; の挙動を正式に規定する文言は存在しないようです。getterの定義しか書かれていません。実験してみた感じでは location.href = url; と同様に振る舞うようです。
経緯
JavaScriptのコードをTypeScriptに置き換えるコードのレビュー中に、 window.location = url; を location.assign(url); に置き換える変更を発見したため、動作の等価性を確認するために調査しました。
参照
- MDNの window.location, location.href, location.assign の記述
- assign側の記事にはSecurityErrorへの言及がある
- 記事執筆時点でのHTML Standard (巨大ページのため注意)