たまたま「⌘+クリック」で新規タブを開かせたい機能の実装をしていて見つけた挙動です。知らなかったのでメモ。
window.open の普通の挙動
const $button = document.querySelector('#button');
$button.addEventListener('click', (e) => {
window.open('./hoge');
)};
のような実装をした場合、通常はボタンクリックで新規タブが開き、新しいタブへ自動的に切り替わります。
修飾キーを押しながらクリックしたときの挙動
同じ実装のまま、ボタンをクリックするときに特定の修飾キーを押し続けてみます。
- macOSの場合はcommandキー
- Windowsの場合はctrlキー
すると新規タブは背面に開きます。
また、同様にshiftキーを押したまま押下するとタブではなく新規ウィンドウで開かれます。
これらの挙動はイベントをpreventDefault
で殺しても発生します。
const $button = document.querySelector('#button');
$button.addEventListener('click', (e) => {
e.preventDefault(); // 防げない
window.open('./hoge');
)};
// これでも防げない
window.addEventListener('keydown', (e) => {
e.preventDefault();
});
旧WebKit系以外のブラウザでは再現せず
これらの現象はSafariとmacOS版のEdgeでも再現しました。一方で、Firefoxや旧エンジンのEdgeでは発生しません。
【追記】 回避策
非常に怪しい実装ですが、window.open
をsetTimeout
の中で実効すると再現しなくなります。
この記事の執筆時点では回避できましたが、将来的に使えなくなる可能性もあります。
const $button = document.querySelector('#button');
$button.addEventListener('click', (e) => {
setTimeout(() => {
window.open('https://codepen.io/hokkey/full/XWbxKOR');
}, 100);
)};
検証用CodePen
See the Pen window.open test for Chrome on pushing a modifier key by y_hokkey (@hokkey) on CodePen.
なぜこの挙動が起きるのか
Chromeではリンクを⌘+クリック(Windowsの場合はctrl+クリック)したときに、タブを背面で開く機能があります。
リンクを新しいバックグラウンド タブで開く | ⌘+リンクをクリック
リンクを新しいウィンドウで開く | shift+リンクをクリック
Chrome のキーボード ショートカット - Google Chrome ヘルプ
最近のサイトはリンクに見える要素でも裏でJavaScriptが動いている場合があるため、DOMのAPIよりも深い部分で新規タブ作成時の挙動を制御しているのでしょう。WHATWGを読むと、そういうブラウザもあるよ〜という注釈が一応書かれていました。
If there is a user agent that supports control-clicking a link to open it in a new tab, and the user control-clicks on an element whose onclick handler uses the window.open() API to open a page in an iframe element, the user agent could override the selection of the target browsing context to instead target a new tab.
https://html.spec.whatwg.org/multipage/window-object.html
Windowオブジェクトは怪しい
この挙動に限らず、Windowオブジェクトの挙動はブラウザ依存の部分が多く、リファレンスを読むだけでは動きを把握できない部分が多い印象です。
たとえば、そもそもwindow.open
で新規ウィンドウになるのか新規タブになるのかも厳密には仕様化されていません。
(たまたまChromeは第3引数の有無でタブ/ウィンドウの挙動を変えてくれているだけで、将来この挙動が変わる可能性もあります。)
要件にウィンドウを開く挙動が絡む場合は実現可能かどうかをよく検討しておいた方が良いかもしれませんね。