DOM

【JavaScript】aタグが外部リンクかどうか判断してクラス名を追加する方法(外部リンクにアイコン追加する方法)

更新日:2023/02/17

Webサイト上のリンクを見て、一目で外部リンクとわかるサイトって最近多いですよね。
僕もやろうと思いましたが、既存の記事を修正するのに時間がかかるためJavaScriptで動的に処理させることにしました。

やることは、リンクが外部リンクかどうかを確認してクラス名を追加するだけです。
最終的に、次のような見た目になりました。

外部リンクのイメージ:TOPへ

 

aタグが外部リンクかどうか判断する方法

JavaScriptでaタグが外部リンクかどうか判断するときは、次のようなコードを記述します。

aタグが外部リンクかどうか判断するコード

const isExternalLink = element => 
    element.href==="" || element.href.startsWith( window.location.origin );

関数が受け付ける引数は、aタグのDOM要素です。
この要素のhref属性が、window.location.originで開始するかどうかをチェックしています。
href属性が記述されていないケースを想定して、空文字のチェックも行っています。

window.location.originは、スキーム(https://)を含めたドメインがセットされています。

ちなみにhtml上のhref属性が相対URLで記述されている場合は、絶対URLに変換されます。
そのため、単純にwindow.location.originと比較するだけで目的を達せられます。

なおhttpサイトのとき同ドメインのhttpsへのリンク、またはその逆は外部サイトへのリンクと判断されます。
同じものと判定する場合は、window.location.hostnameにドメイン名がセットされているので、"http://""https://"の後ろに連結して比較します。

aタグが外部リンクかどうか判断するコード

const isExternalLink = element => 
    element.href==="" || element.href.startsWith( `http://${window.location.hostname}/`)
    || element.href.startsWith( `https://${window.location.hostname}/`);

 

外部リンクにアイコンを表示する

外部リンクにアイコンがついていると、外部サイトに飛ぶことをユーザーに伝えることができます。
親切なサイトだと思ってもらえるかもしれません。

アイコンはクラスを追加してcssで表示するのが手っ取り早いですが、htmlに記述するのを忘れることが多いです。
そこで前項の関数を使って、自動的に外部リンクにアイコンを表示してみましょう。

aタグの子ノードにimgタグ等が無いかチェック

その前に、画像リンクにアイコンを表示するとレイアウトが乱れる可能性があるので、今回は除外しておきます。
同様に、div等で構成された複雑な構造を持つものも除外します。

次のコードは、子ノードがテキストのみかどうかをチェックする関数です。

子ノードがテキストのみかどうかをチェック

const allowedTextTag = ["SPAN","P"];  // spanとpは許容
const allowedNodeType = [ // テキスト、コメント、セクション(<!CDATA[[ … ]]>)は許容
    Node.TEXT_NODE,Node.CDATA_SECTION_NODE,Node.COMMENT_NODE];

const isTextNode = element =>Array.from(element.childNodes)
                     .every(e=>{
                        if( allowedNodeType.indexOf(e.nodeType) >= 0 ) return true;
                        return e.nodeType === Node.ELEMENT_NODE
                                && allowedTextTag.indexOf( e.tagName ) >= 0
                                && isTextNode( e );
                     });

spanまたはpタグおよびコメントは、テキストとみなしています。

■ノードのタイプは次のリンク先を参考にしています。
https://developer.mozilla.org/ja/docs/Web/API/Node/nodeType

外部リンクにクラスを追加

ここまでに紹介したisExternalLink()とisTextNode()を使って外部リンクの判定を行い、クラスを追加します。

次のようなコードになります。

外部リンクにクラスを追加するコード

Array.from(document.getElementsByTagName("a"))
    .forEach( a => isExternalLink(a) && isTextNode( a ) 
                    ? a.classList.add("exlink") : null );

ここでは、クラスに"exlink"を追加しています。

スタイルを適用

CSSの.exlink:after要素に、スタイルを適用します。

a.exlink:after{
    content: "[↑]";
    font-size: 0.5em;
    color: blue;
    font-weight: bold;
    position: relative;
    top: -10px;
    display: inline-block;
}

適用すると、次のようなイメージになります。

適用後のイメージ:TOPへ

これで十分な気もしますが、次のようなSVG画像を作成したので使ってみます。

外部リンクSVGイメージ

次のリンクをクリックするとダウンロードできます。
https://affi-sapo.com/download/extlink.svg

スタイルは次のようになります。

a.exlink:after{
    content: "";
    -webkit-mask-image:url("/img/extlink.svg");
    -webkit-mask-size: cover;
    mask-imag: url("/img/extlink.svg");
    mask-size: cover;
    background-color: blue;
    display: inline-block;
    height: 0.8em;
    width: 0.8em;
    top: 5px;
    position: relative;
    margin: 0 3px;
}

SVGの色変更は、SVG画像でmaskして背景色で塗りつぶすイメージですね。

あと、chromeは-webkit-が必要だとか記述する順番が大事とかいろいろあるみたいです。

適用すると、次のようなイメージになります。

適用後のイメージ:TOPへ

 

ソースコード

今回作成したコードを、少し改良して掲載します。

cssは変更なしです。
webkit-mask-imagemask-imagurlは、環境に合わせて変更してください。

add-externallink-icon.css

a.exlink:after{
    content: "";
    -webkit-mask-image:url("/img/extlink.svg");
    -webkit-mask-size: cover;
    mask-imag: url("/img/extlink.svg");
    mask-size: cover;
    background-color: blue;
    display: inline-block;
    height: 0.8em;
    width: 0.8em;
    top: 5px;
    position: relative;
    margin: 0 3px;
}

jsは、defer属性での読み込みを想定してDOMContentLoaded待ちを外しています。
また、外部リンクが存在するときのみ、cssを動的に読み込んでいます。

add-externallink-icon.js

(()=>{
    const isExternalLink = element => 
        !(element.href==="" || element.href.startsWith( `http://${window.location.hostname}/`)
        || element.href.startsWith( `https://${window.location.hostname}/`));

    const allowedTextTag = ["SPAN","P"];
    const allowedNodeType = [Node.TEXT_NODE,Node.CDATA_SECTION_NODE,Node.COMMENT_NODE];

    const isTextNode = element =>Array.from(element.childNodes)
                        .every(e=>{
                            if( allowedNodeType.indexOf(e.nodeType) >= 0 ) return true;
                            return e.nodeType === Node.ELEMENT_NODE
                                    && allowedTextTag.indexOf( e.tagName ) >= 0
                                    && isTextNode( e );
                        });

    const count = Array.from(document.getElementsByTagName("a"))
            .reduce( (count,a) => {
                if( isExternalLink(a) && isTextNode( a ) ){
                    a.classList.add("exlink");
                    return count + 1;
                }
                return count;
            },0);
    if( count > 0 ){
        const link = document.createElement( "link" );
        link.rel = "stylesheet";
        link.href = "/css/add-externallink-icon.css";
        document.head.appendChild(link);
    }
})();

htmlのscriptタグは、defer属性を記述します。

HTML

<script defer src="/js/add-externallink-icon.js"></script>

更新日:2023/02/17

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。