htsign's blog

ノンジャンルで書くつもりだけど技術系が多いんじゃないかと思います

【修正版】httpやttpやtpなリンクのないURLにリンクを付けるありきたりなスクリプトを回りくどいコードで書いた

前回載せたスクリプトのバグ修正が完了したので再掲。

原因はなんとなくだけど検討ついたので、とりあえず書き換えてみたら見事正常に機能するようになりました。

while ((regRes = pattern.exec(self.textContent)) !== null) {
  var a = document.createElement("a");
  a.href = "http" + regRes[1];
  range.setStart(self, regRes.index);
  range.setEnd(self, pattern.lastIndex);
  range.surroundContents(a);
}

この部分でせっかくregRespatternにオフセットがセットされたのに、
range.surroundContents(a)で文字列長を変えてしまっていたのが原因だったのではないかと。


ですので、マッチ結果を一旦スタックに保存して、1つずつ取り出し後ろから補正をかけていくことで
オフセットに影響が出ないようにしてみました。

というわけで

今回のコードは以下です。

(function(){
   // Rangeが使えなければ意味が無いので、ここで一旦判定をしています。
  if (!document.createRange)
    return window.alert("このスクリプトを実行するために必要なAPIが使用できません。");

  var range = document.createRange();

  (function(currentNode){
    var childs = currentNode.childNodes;
    var pattern= /h?t?tp(s?:\/\/(?:[\w-]+|[^ -~。-゚]+)\.[a-zA-Z]{2,4}[^\s ]*)/g;
    var regRes = [];
    
    for (var i = 0, l = childs.length; i < l; i++) {
      var self = childs[i];
      if (self.childNodes && self.childNodes.length) { // 子ノードがいる場合は再帰
        arguments.callee(self);
      }

      if (self.nodeType !== 3) continue; // テキストノードでなければ次へ

      var matchStack = [];

      while (regRes = pattern.exec(self.textContent)) { // マッチがなくなるまでループしてスタック
        matchStack.push({
          "mUrl"   : "http" + regRes[1],
          "pStart" : regRes.index,
          "pEnd"   : pattern.lastIndex
        });
      }
      while (matchStack.length >= 1) { // 後ろから1つずつ取り出して変更を適用
        var item = matchStack.pop();
        var a = document.createElement("a");
        a.href = item.mUrl;
        range.setStart(self, item.pStart);
        range.setEnd(self, item.pEnd);
        range.surroundContents(a);
      }
    }
  })(document.body);

  range.detach();
})();
ブックマークレット

これでおそらくバグとしてはなくなったかな。
URLテキストがあると問答無用でAタグで囲ってしまうのは変わらず。