Chromeはwindow.openするときに修飾キーの影響を受ける

たまたま「⌘+クリック」で新規タブを開かせたい機能の実装をしていて見つけた挙動です。知らなかったのでメモ。

## window.open の普通の挙動

```js
const $button = document.querySelector('#button');
$button.addEventListener('click', (e) => {
  window.open('./hoge');
)};
```

のような実装をした場合、通常はボタンクリックで新規タブが開き、新しいタブへ自動的に切り替わります。

![window.openの通常の挙動を描いたGIF動画です。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48238/61e05b04-cc21-f759-cc78-1d1ee26aba8e.gif)

## 修飾キーを押しながらクリックしたときの挙動

同じ実装のまま、ボタンをクリックするときに特定の修飾キーを押し続けてみます。

  - macOSの場合はcommandキー
  - Windowsの場合はctrlキー

すると新規タブは**背面**に開きます。

![macOSのChromeでcommandキーを同時押ししてwindow.openしたときのGIF画像です。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48238/8f5bff1f-a0a9-ffa2-223a-ee095c683bd9.gif)

また、同様にshiftキーを押したまま押下するとタブではなく**新規ウィンドウ**で開かれます。

これらの挙動はイベントを`preventDefault`で殺しても発生します。

```js
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では発生しません。

![macOSのFirefoxでcommandキーを同時押ししてwindow.openしたときのGIF画像です。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48238/c9dbd894-d080-42fb-4dbe-5aeda38bcf80.gif)

### 【追記】 回避策

非常に怪しい実装ですが、`window.open`を`setTimeout`の中で実効すると再現しなくなります。
この記事の執筆時点では回避できましたが、将来的に使えなくなる可能性もあります。

```js
const $button = document.querySelector('#button');
$button.addEventListener('click', (e) => {
  setTimeout(() => {
    window.open('https://codepen.io/hokkey/full/XWbxKOR');
  }, 100);
)};
```

### 検証用CodePen

<p class="codepen" data-height="300" data-theme-id="23059" data-default-tab="js,result" data-user="hokkey" data-slug-hash="XWbxKOR" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="window.open test for Chrome on  pushing a modifier key">
  <span>See the Pen <a href="https://codepen.io/hokkey/pen/XWbxKOR">
  window.open test for Chrome on  pushing a modifier key</a> by y_hokkey (<a href="https://codepen.io/hokkey">@hokkey</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## なぜこの挙動が起きるのか

Chromeではリンクを⌘+クリック(Windowsの場合はctrl+クリック)したときに、タブを背面で開く機能があります。

> リンクを新しいバックグラウンド タブで開く | ⌘+リンクをクリック
> リンクを新しいウィンドウで開く | shift+リンクをクリック
> [Chrome のキーボード ショートカット - Google Chrome ヘルプ](https://support.google.com/chrome/answer/157179?hl=ja)

最近のサイトはリンクに見える要素でも裏で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引数の有無でタブ／ウィンドウの挙動を変えてくれているだけで、将来この挙動が変わる可能性もあります。)

要件にウィンドウを開く挙動が絡む場合は実現可能かどうかをよく検討しておいた方が良いかもしれませんね。