【问题标题】:Wrap <a> tags around http text将 <a> 标签包裹在 http 文本周围
【发布时间】:2011-07-06 16:38:40
【问题描述】:

如何找到页面上以 http:// 开头的每个单词并在其周围加上标签?

我可以使用正则表达式之类的东西吗?

【问题讨论】:

    标签: javascript jquery regex string


    【解决方案1】:

    我不是很实际,但你可以试试

    $('a([href^="http://"])').each( function(){
            //perform your task
        })
    

    【讨论】:

      【解决方案2】:

      这是 jQuery 不能直接帮助您的少数几件事之一。您基本上必须遍历 DOM 树并检查文本节点 (nodeType === 3);如果您找到一个包含要包装的目标文本的文本节点(“http://.....”,无论您要应用什么规则),然后 split 文本节点(使用splitText) 分成三部分(字符串前面的部分、字符串后面的部分和字符串后面的部分),然后将a 元素放在其中的第二部分周围。

      这听起来有点复杂,但其实并没有那么糟糕。它只是一个递归下降 walker 函数(用于通过 DOM 工作),一个正则表达式匹配以查找要替换的内容,然后调用 splitTextcreateElementinsertBeforeappendChild

      这是一个搜索固定字符串的示例;只需为“http://”添加您的正则表达式匹配:

      walk(document.body, "foo");
      
      function walk(node, targetString) {
        var child;
      
        switch (node.nodeType) {
          case 1: // Element
            for (child = node.firstChild;
                 child;
                 child = child.nextSibling) {
              walk(child, targetString);
            }
            break;
      
          case 3: // Text node
            handleText(node, targetString);
            break;
        }
      }
      
      function handleText(node, targetString) {
        var start, targetNode, followingNode, wrapper;
      
        // Does the text contain our target string?
        // (This would be a regex test in your http://... case)
        start = node.nodeValue.indexOf(targetString);
        if (start >= 0) {
          // Split at the beginning of the match
          targetNode = node.splitText(start);
      
          // Split at the end of the match
          followingNode = targetNode.splitText(targetString.length);
      
          // Wrap the target in an element; in this case, we'll
          // use a `span` with a class, but you'd use an `a`.
          // First we create the wrapper and insert it in front
          // of the target text.
          wrapper = document.createElement('span');
          wrapper.className = "wrapper";
          targetNode.parentNode.insertBefore(wrapper, targetNode);
      
          // Now we move the target text inside it
          wrapper.appendChild(targetNode);
      
          // Clean up any empty nodes (in case the target text
          // was at the beginning or end of a text ndoe)
          if (node.nodeValue.length == 0) {
            node.parentNode.removeChild(node);
          }
          if (followingNode.nodeValue.length == 0) {
            followingNode.parentNode.removeChild(followingNode);
          }
        }
      }
      

      Live example


      更新:如果在同一个文本节点中有多个匹配项,则上面没有处理它(doh!)。哦,见鬼,我做了一个正则表达式匹配——你必须调整正则表达式,并且可能对每个匹配做一些后处理,因为这里的东西太简单了。但这是一个开始:

      // The regexp should have a capture group that
      // will be the href. In our case below, we just
      // make it the whole thing, but that's up to you.
      // THIS REGEXP IS ALMOST CERTAINLY TOO SIMPLISTIC
      // AND WILL NEED ADJUSTING (for instance: what if
      // the link appears at the end of a sentence and
      // it shouldn't include the ending puncutation?).
      walk(document.body, /(http:\/\/[^ ]+)/i);
      
      function walk(node, targetRe) {
        var child;
      
        switch (node.nodeType) {
          case 1: // Element
            for (child = node.firstChild;
                 child;
                 child = child.nextSibling) {
              walk(child, targetRe);
            }
            break;
      
          case 3: // Text node
            handleText(node, targetRe);
            break;
        }
      }
      
      function handleText(node, targetRe) {
        var match, targetNode, followingNode, wrapper;
      
        // Does the text contain our target string?
        // (This would be a regex test in your http://... case)
        match = targetRe.exec(node.nodeValue);
        if (match) {
          // Split at the beginning of the match
          targetNode = node.splitText(match.index);
      
          // Split at the end of the match.
          // match[0] is the full text that was matched.
          followingNode = targetNode.splitText(match[0].length);
      
          // Wrap the target in an `a` element.
          // First we create the wrapper and insert it in front
          // of the target text. We use the first capture group
          // as the `href`.
          wrapper = document.createElement('a');
          wrapper.href = match[1];
          targetNode.parentNode.insertBefore(wrapper, targetNode);
      
          // Now we move the target text inside it
          wrapper.appendChild(targetNode);
      
          // Clean up any empty nodes (in case the target text
          // was at the beginning or end of a text ndoe)
          if (node.nodeValue.length == 0) {
            node.parentNode.removeChild(node);
          }
          if (followingNode.nodeValue.length == 0) {
            followingNode.parentNode.removeChild(followingNode);
          }
      
          // Continue with the next match in the node, if any
          match = followingNode
            ? targetRe.exec(followingNode.nodeValue)
            : null;
        }
      }
      

      Live example

      【讨论】:

      • Tim:我给了你时间来扩展它,你做到了:) 太棒了。谢谢TJ!!如果我能投票更多,我会的。
      • 那么,呃,我们能在这个上获得更多的选票吗?这比我的小一公吨好,“这已经解决了。”
      • @TJ:对不起,伙计...更改此设置只会将跨度环绕在“http://”文本周围,而不是链接的其余部分:walk(document.body, "http:// ");请指教..!非常感谢
      • @Tim:这就是我的意思是你必须如何扩展它以支持进行正则表达式匹配或类似的。我只是在解决如何查找文本并将其包装在元素中;为您想要的 http 模式进行匹配(可能使用正则表达式)作为练习留给读者,如 cmets 中所示。 :-)
      • @TJ: 哈哈,好吧,很公平 :) 我想我可以从这里解决 - 谢谢你的帮助。
      【解决方案3】:

      我非常不同意 jQuery 可以在这里找到解决方案。当然,您必须对一些 textNode 元素属性感到沮丧和肮脏,但是使用 jQuery 库可以使您在拆分匹配的节点后再次将 DOM 重新组合在一起。

      以下代码内嵌记录以解释所采取的操作。我把它写成一个 jQuery 插件,以防你只是想把它移到别处。这样,您可以确定要为其转换 URL 的元素的范围,或者您可以简单地使用 $("body") 选择器。

      (function($) {
          $.fn.anchorTextUrls = function() {
              // Test a text node's contents for URLs and split and rebuild it with an achor
              var testAndTag = function(el) {
                  // Test for URLs along whitespace and punctuation boundaries (don't look too hard or you will be consumed)
                  var m = el.nodeValue.match(/(https?:\/\/.*?)[.!?;,]?(\s+|"|$)/);
      
                  // If we've found a valid URL, m[1] contains the URL
                  if (m) {
                      // Clone the text node to hold the "tail end" of the split node
                      var tail = $(el).clone()[0];
      
                      // Substring the nodeValue attribute of the text nodes based on the match boundaries
                      el.nodeValue = el.nodeValue.substring(0, el.nodeValue.indexOf(m[1]));
                      tail.nodeValue = tail.nodeValue.substring(tail.nodeValue.indexOf(m[1]) + m[1].length);
      
                      // Rebuild the DOM inserting the new anchor element between the split text nodes
                      $(el).after(tail).after($("<a></a>").attr("href", m[1]).html(m[1]));
      
                      // Recurse on the new tail node to check for more URLs
                      testAndTag(tail);
                  }
      
                  // Behave like a function
                  return false;
              }
      
              // For each element selected by jQuery
              this.each(function() {
                  // Select all descendant nodes of the element and pick out only text nodes
                  var textNodes = $(this).add("*", this).contents().filter(function() {
                      return this.nodeType == 3
                  });
      
      
                  // Take action on each text node
                  $.each(textNodes, function(i, el) {
                      testAndTag(el);
                  });
              });
          }
      }(jQuery));
      
      $("body").anchorTextUrls(); //Sample call
      

      请记住,鉴于我编写此代码以填充 textNodes 数组的方式,该方法将查找所有后代文本节点,而不仅仅是直接子文本节点。如果您希望它仅在特定选择器中的文本中替换 URL,请删除添加所选元素的所有后代的 .add("*", this) 调用。

      这是fiddle example

      【讨论】:

      • @Tim 对于它的价值,正则表达式会尽可能地考虑常见的标点符号或空格,因为它可能会出现在 URL 的末尾,所以它不必是专门的空格分隔。
      • 考虑到您使用过 jQuery(+less 代码)并编写了 REGEX FROM HELL - 我会将其标记为正确答案。感谢您的时间和 JS 和 jQ 的出色表现! :D
      • @Tim 不要看得太近,否则你会被吃掉的!我其实并没有全部写出来,只是在后面写了边界检查。该基地是keevkillaSnipplr从工作中借来的。应该早点到账。
      • @Tim 我刚刚对 textNodes 选择器做了一个小编辑,这很重要。而不是 .find() 我需要使用 .add() 来确保顶层的文本节点子节点包含在标记中。如果您只想从选定元素而不是其子元素中获取文本节点,只需删除 .add() 调用。此外,事实证明我的边界正则表达式优于长期人为的混乱,因为它很好地找到了 URL 的限制并且只需要起点。
      • 啊,是的,这好多了——之前的正则表达式规则是让任何字符串与 .进入链接:) 现在就像一个魅力。非常感谢!
      猜你喜欢
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 2020-06-28
      • 1970-01-01
      • 1970-01-01
      • 2013-09-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多