注意! この記事はQiitaにて公開されていた内容をimportしたものです。
これらの内容は場合によっては陳腐化していて役に立たなくなっていたり、有害であったり、現在の著者の主張と異なることがあります。
皆様の判断の上でご利用いただけますと幸いです(度を超してヤバいものは著者に連絡して頂ければ対応します m(_ _)m)


はじめに

AngularJSでリンクを新しいタブで開くにはどうすればよいでしょうか。

※以下のStack Overflowを参考にしましたが、紹介されているDirectiveだと、ある条件下でうまくいかないので、この文章の4番目ではその対策を記します。

1. aタグのtarget属性に_blankを指定する

<a href="http://example.com/" target="_blank">新しいタブで開く</a>

これは別にAngularJS関係無いですね。

2. 全てのリンク(href属性)を新しいタブで開く

例えば、「全てのリンクでtarget="_blank"を付けたような振る舞いをさせたい」ときはどうでしょうか。

app.directive('href', function() {
  return {
    compile: function(element) {
      element.attr('target', '_blank');
    }
  };
});

これで定義されているリンクが新しいタブで開かれるようになります。

(最初これを見た時は「賢い!」と思わず唸ってしまいました)

3. 特定の範囲のリンクだけを新しいタブで開く

全てのリンクを新しいタブで開きたくない、でもある範囲(特定の属性が記述されたdivタグ内)だけは新しいタブで開きたい場合はどうでしょうか。

app.directive('targetBlank', function() {
  return {
    compile: function(element) {
      var elems = (element.prop("tagName") === 'A') ? element : element.find('a');
      elems.attr("target", "_blank");
    }
  };
});

4行目の判定で、このDirectiveが記されているタグがAタグか、それ以外かによって振る舞いを変えています。

<a href="http://example.com/" target-blank>新しいタブで開く</a>

とすると、ほぼ 2番の説明と同じ振る舞いになります。

<div target-blank>
    <a href="http://example.com/">新しいタブで開く</a><br>
    <a href="http://example.com/">新しいタブで開く</a><br>
    <a href="http://example.com/">新しいタブで開く</a><br>
</div>

とすると、divタグ内のaタグが全て新しいタブで開かれるようになります。

3の問題点

ここまではStackOverflowに記載されています。ただ、このやり方だと以下のような場合にaタグが新しいタブで開かれません。

<!-- btfMarkdownディレクティブは、指定された変数内をMarkdownとして解釈しHTMLにパースします -->
<div btf-markdown="event.description" target-blank></div>

つまり、Directiveのcompile処理が終わった後ではまだ$scopeが解決されておらず、中身は空っぽです。よってaタグにtarget属性を付与することが出来ず、新しいタブで開くことができなくなります。

これを解決するには、DOMの変更を検知して処理させるようにする必要があります。

ちなみにconsole.log()などで出力しながらデバッグすると一見DOMが生成されているように見えるのですが、ログ出力の直後にブレークポイントを貼って確認すると中身が空っぽでした。結構ハマった。。。

4. 特定の範囲のリンクだけを新しいタブで開く(改良版)

app.directive('targetBlank', function () {
  return {
    restrict: 'A',
    link: function (scope, element, attr) {
      if (element.prop("tagName") === 'A') {
        element.attr("target", "_blank");
      } else {
        var elems = element.find('A');
        element.bind('DOMSubtreeModified', function () {
          elems = angular.element(this).find('A');
          elems.attr("target", "_blank");
        });
      }
    }
  };
});

3の三項演算子をif文に展開し、aタグではないとき(divなど)は、そのタグのDOMのSubtree(ぶら下がっているDOM)が変更された時にイベントを実行するようにします。 実行されるイベントは、サブツリーにあるAタグを拾い、その全てにtarget属性を付けるというもの(3でのメインの処理と同じです)。

DOMSubtreeModified自体がモダンなブラウザでのみ使えるようなので注意が必要です。どうしても、というときはHTMLを生成する部分でtarget属性を付与するのがよろしいかと思います(そのほうが軽いと思いますが、今回の趣旨とは離れちゃいますね)。