プロフィール

髭山髭人(ひげひと)

自分の書いた記事が、一人でも誰かの役に立てば...
活動信条の一つとして「貴方のメモは、誰かのヒント」というのがあります。

このサイトについて

本家HP packetroom.net から切り離した いわゆる技術メモ用のブログで、無料レンタルサーバーにて運用しています。広告表示はその義務なのでご容赦。
XREA さんには長年お世話になっています

jsで文中に複数回出現するURLをハイパーリンクのaタグで囲む

概要など

  • やりたいこと
    自作のチャットサービス等で、メッセージ内のURL部分をハイパーリンク化し、
    それ以外は任意ノード ( <span> 等 ) で囲みたい。
    でも .replace() とか .outerHTML とか使った直接文字列置換はしたくない

  • 実現イメージ
    "あいうえおhttps://example.com/ やらhttps://google.com/ など"
    ...といった文字列から、以下の様なノードを構成

    <div>
      <span>あいうえお</span>
      <a href="https://example.com/">https://example.com/</a>
      <span> やら</span>
      <a href="https://google.com/">https://google.com/</a>
      <span> など</span>
    </div>
  • 留意点
    URLの正規表現は適当にググったものを使ってます。
    ( ※ 何が限りなく正解に近いのか、よくわかっていないので )
    「javascript URL 正規表現」 で検索

実処理

URL検出の正規表現を使いつつも、
.split()セパレーターを消さない分割方法 を足がかりにするのが楽かな~と思いました

{
    const _text = "この文章にはいくつかの https://example.com や https://www.google.co.jp/ などの URL が含まれています。";
    const _urlRegex = /(https?:\/\/[\w/:%#\$&\?\(\)~\.=\+\-]+)/;
    const _nodes = ((_chatMsg)=>{
        let _parts = _chatMsg.split(_urlRegex);
        let _nodeArr = [];
        _parts.forEach(_txt=>{
            if(_txt.length <= 0){
                return;
            }
            else if(URL.canParse(_txt)){
                const _a = document.createElement("a");
                _a.href = _txt;
                _a.target = "_blank";
                _a.textContent = _txt;
                _nodeArr.push(_a);
            }else{
                const _span = document.createElement("span");
                _span.textContent = _txt;
                _nodeArr.push(_span);
            }
        });
        return _nodeArr;
    })(_text);
    console.log(_nodes);
    // 後は _nodes.forEach() とかで、レンダリングしたい ノードに対して .appendChild() すれば良さそう
}

個人的ポイント

  • .split() でセパレータを含める
    MDN - String.prototype.split() > RegExp を使った分割でセパレーターの一部を結果に含める

    正規表現部分を () で囲むことで、セパレーター部分を含めた分割配列を得られます。
    ( ※ この記事を書くまで知りませんでした orz )

    {
      const _txt = "あいうえおhttps://example.com/ やらhttps:google.com/ など";
      let _result_1 = _txt.split(/(https?:\/\/[\w/:%#\$&\?\(\)~\.=\+\-]+)/);
      let _result_2 = _txt.split(/https?:\/\/[\w/:%#\$&\?\(\)~\.=\+\-]+/);
    
      console.log(_result_1);
      // (5) ['あいうえお', 'https://example.com/', ' やら', 'https://  google.com/', ' など']
    
      console.log(_result_2);
      // (3) ['あいうえお', ' やら', ' など']
    }
  • URL判定部分 ( 正規表現部分とで、URL判定を実質2回しちゃってますが… )
    MDN - URL: canParse()
    URL.canParse() 判定に任せることで、
    旧来のtry-catchnew URL(); を囲う方法から脱却できた

    ちなサポート状況
    Can I use - URL.canParse