HTML の要素に onclick="..." のように直接処理を書く方法は手軽ですが、HTML と JavaScript が混ざって読みにくくなります。ここでは、リンクをクリックしたときに小さな別ウィンドウを開く処理を例に、イベントを JavaScript の側から登録する書き方を紹介します。

onclick 属性に書いた場合

<a href="help.html" onclick="window.open('help.html', 'help', 'width=400,height=500'); return false;">ヘルプ</a>

この書き方には、次のような問題があります。

  • 同じ動きをするリンクが増えるたびに、同じコードを書き写すことになる
  • URL を href と onclick の 2 か所に書くので、片方だけ直すと食い違う
  • HTML の属性の中に JavaScript の文字列を書くため、引用符の入れ子を間違えやすい
  • Content Security Policy (CSP) で HTML の中に書いたスクリプトを禁止すると、動かなくなる

addEventListener で登録する

HTML には、目印になる class だけを付けます。

<a href="help.html" class="js-popup">ヘルプ</a>

JavaScript の側で、目印の付いたリンクにイベントを登録します。

document.querySelectorAll("a.js-popup").forEach((link) => {
  link.addEventListener("click", (event) => {
    event.preventDefault();
    window.open(link.href, "help", "width=400,height=500");
  });
});
  • event.preventDefault() で、リンク本来の動き (ページの移動) を止めます。onclick 属性に書く return false; に当たります
  • URL は href から読むので、1 か所に書くだけで済みます
  • JavaScript が動かない環境でも、ふつうのリンクとして開けます

addEventListener は、同じ要素に複数の処理を登録できます。onclick 属性や element.onclick = ... は処理を 1 つしか持てないため、あとから別の処理を代入すると、前の処理が消えてしまいます。

要素が多いときは、親要素でまとめて受け取る

リンクの数が多いときや、あとから要素を追加するときは、親要素 (ここでは document) にイベントを 1 つだけ登録し、クリックされた要素を調べる方法もあります。イベントの委譲 (delegation) と呼ばれる書き方です。

document.addEventListener("click", (event) => {
  const link = event.target.closest("a.js-popup");
  if (!link) return;
  event.preventDefault();
  window.open(link.href, "help", "width=400,height=500");
});

クリックのイベントは、クリックされた要素から親要素へと順に伝わります (バブリング)。closest() で、クリックされた位置からいちばん近い a.js-popup を探しています。あとから追加したリンクにも、そのまま効きます。

別ウィンドウを開く前に考えたいこと

スマートフォンのブラウザーでは、window.open() で開いたページはタブとして開くことが多く、大きさの指定は効きません。また、クリックなどの操作の直後でないときに開こうとすると、ポップアップブロックが働きます。

新しいタブで開きたいだけなら、JavaScript は要りません。target="_blank" で足ります。今のブラウザーでは、target="_blank" で開いたページから元のページを操作できないように扱われます (rel="noopener" を付けたときと同じ)。

<a href="help.html" target="_blank">ヘルプ (新しいタブで開きます)</a>

新しいタブで開くリンクには、そのことを文字で書き添えておくと、利用者が戸惑いません。

まとめ

  • HTML には目印 (class や data 属性) だけを書き、処理は JavaScript の addEventListener で登録する
  • リンク本来の動きを止めるには event.preventDefault() を使う
  • 要素が多いときや、あとから追加するときは、親要素でまとめて受け取る (イベントの委譲)
  • 新しいタブで開くだけなら target="_blank" で足りる

参考