JavaScript RegExp#hasgtag替换为html中没有超hashlink的链接



我想用JavaScript或jQuery 将#hashtag文本替换为<a href="http://example.com/foo=hashtag"> #hasgtag</a>

我试过了:

   <!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Try it</button>
<p id="demo">Please visit #Microsoft! #facebook <a href="#link"> Somelink</a>
</p>
<script>
function myFunction() {
    var str = document.getElementById("demo").innerHTML; 
   var txt = str.replace(/#w+.?w+/g,"<a href="http://example.com?hashtag=selectedteg">#Selected</a> ");
    document.getElementById("demo").innerHTML = txt;
}
</script>
</body>
</html>

但是这个结果回来了。。。

<p id="demo">Please visit <a href="http://example.com?hashtag=selectedteg">#Selected</a> ! <a href="http://example.com?hashtag=selectedteg">#Selected</a>  <a href="&lt;a href=" http:="" example.com?hashtag="selectedteg&quot;">#Selected</a> "&gt; Somelink
</p>

我希望结果像一样

<p id="demo">Please visit <a href="http://example.com?hashtag=Microsoft">#Microsoft</a> ! <a href="http://example.com?hashtag=facebook">#facebook</a>  <a href="#link">Somelink</a>
</p>

哇!这是一个令人惊讶的难题,尽管乍一看应该很简单。

问题是,严格来说,您的需求要求只处理文本节点,以将标签转换为链接。现有的HTML不应该被触摸。

一种天真的方法(见其他答案)会试图设计一个复杂的正则表达式来躲避HTML。尽管这似乎适用于某些情况,甚至几乎所有的实际情况,但它绝对不是万无一失的。正则表达式的功能根本不足以完全解析HTML;这是一门过于复杂的语言。请参阅RegEx中出色且相当著名的Stack Overflow答案,它匹配除XHTML自包含标记之外的开放标记。它不可能完美地完成,而且应该永远不要。

相反,正确的方法是使用递归JavaScript函数遍历HTML树,并将所有目标文本节点替换为其自身的处理版本,重要的是,这可能涉及在文本节点内引入(非文本)HTML标记。

jQuery可以用最小的复杂性来完成这项任务,尽管任务本身需要一定的复杂性,老实说,这是无法避免的。正如我所说,这是一个令人惊讶的难题。

HTML

<button onclick="tryItClick()">Try it</button>
<p id="demo">Please visit #Microsoft! #facebook <a href="#link">Somelink</a>
</p>

JavaScript

if (!window.Node) {
    window.Node = {
        ELEMENT_NODE                :  1,
        ATTRIBUTE_NODE              :  2,
        TEXT_NODE                   :  3,
        CDATA_SECTION_NODE          :  4,
        ENTITY_REFERENCE_NODE       :  5,
        ENTITY_NODE                 :  6,
        PROCESSING_INSTRUCTION_NODE :  7,
        COMMENT_NODE                :  8,
        DOCUMENT_NODE               :  9,
        DOCUMENT_TYPE_NODE          : 10,
        DOCUMENT_FRAGMENT_NODE      : 11,
        NOTATION_NODE               : 12
    };
} // end if
window.linkify = function($textNode) {
    $textNode.replaceWith($textNode.text().replace(/#(w+.?w+)/g,'<a href="http://example.com?hashtag=$1">#$1</a>'));
}; // end linkify()
window.processByNodeType = function($cur, nodeTypes, callback, payload ) {
    if (!nodeTypes.length)
        nodeTypes = [nodeTypes];
    for (var i = 0; i < $cur.length; ++i) {
        if ($.inArray($cur.get(i).nodeType, nodeTypes ) >= 0)
            callback($cur.eq(i), $cur, i, payload );
        processByNodeType($cur.eq(i).contents(), nodeTypes, callback, payload );
    } // end for
} // end processByNodeType()
window.tryItClick = function(ev) {
    var $top = $('#demo');
    processByNodeType($top, Node.TEXT_NODE, linkify );
}; // end tryItClick()

http://jsfiddle.net/3u6jt988/

在可能的情况下编写通用代码总是很好的,以最大限度地提高可重用性,而且通常是简单性(尽管过多的通用性可能会导致过度的复杂性;这是有代价的)。我编写processByNodeType()是一个非常通用的函数,它使用jQuery遍历HTML节点树的子树,从给定的顶部节点开始,向下遍历。该函数的目的是做一件事,也只是做一件事情:为遍历过程中遇到的所有节点调用给定的callback()函数,这些节点的nodeType等于nodeTypes中给定的白名单值之一。这就是为什么我在代码的顶部包含了节点类型常量的枚举;看见http://code.stephenmorley.org/javascript/dom-nodetype-constants/.

此函数的功能非常强大,可以在响应单击事件时调用一次,将#demo元素作为顶部节点传递给它,仅将Node.TEXT_NODE节点列入白名单,并提供linkify()作为回调。

当调用linkify()时,它只接受它的第一个参数,即节点本身,并进行您设计的完全替换(尽管必须添加捕获组反向引用才能用标签正确替换文本)。最后一块拼图是用任何需要的新节点结构来替换文本节点,以实现替换,如果确实有一个标签要替换,这将涉及在旧的纯文本节点上引入新的HTML结构。幸运的是,jQuery的惊人之处是无限的,它让这件事变得非常容易,只需一句漂亮的话就可以完成:

$textNode.replaceWith($textNode.text().replace(/#(w+.?w+)/g,'<a href="http://example.com?hashtag=$1">#$1</a>'));

正如您所看到的,对text()的一次调用可以获得纯文本节点的文本内容,然后调用字符串对象上的replace()函数,用HTML替换任何标签,然后jQuery的replaceWith()方法允许我们用生成的HTML替换整个文本节点,或者在不执行替换的情况下保留原始纯文本。

参考文献

  • http://blog.alexanderdickson.com/javascript-replacing-text
  • http://api.jquery.com/children/
  • http://code.stephenmorley.org/javascript/dom-nodetype-constants/
  • http://api.jquery.com/replacewith/
  • RegEx匹配除XHTML自包含标记之外的开放标记

您必须捕获带括号的文本,但也必须只捕获文本,而不是html标记中的内容。请参阅函数中的注释。

function hashtagReplace() {
  
    var text = document.getElementById("demo").innerHTML; 
	//you have first to capture the text, to avoid the capture of #link in your example 
	//The text is somewhare between the start of the input, or ">" and the end of the input and "<"
	var result = text.replace( /(^.|>)([^<]*)(<|.$)/g ,function(match, start, capture, end ){
		//then you capture the hashtag text, and replace all the hashtag (#+hashtag_word) by the link. 
		//you set the text captured by the parentethis with $1
		var hashtagsReplaced= (start+capture+end).replace(/#(w+)/g,"<a href="http://example.com?hashtag=$1">#$1</a>")
        
	  //you return all the html 
          return hashtagsReplaced;
	});
	//finally you replace the html in the document
        document.getElementById("demo").innerHTML = result;
}
<!DOCTYPE html>
<html>
<body>
<button onclick="hashtagReplace()">Try it</button>
<p id="demo">#Microsoft Please visit #Microsoft ! #facebook <a href="#link"> Somelink</a>
</p>
</body>
</html>

您需要捕获组,然后在替换中使用它。类似于:

var txt = str.replace(/#(w+.?w+)/g,"<a href="http://example.com?hashtag=$1">#$1</a> ");

在要捕获的零件周围放上括号使其成为一个捕获组,然后捕获的组将插入替换字符串中的$1标记处。

当然,更大的问题是您的regex与您现有的链接相匹配,并试图在其中进行替换,这完全会把事情搞砸。这就是为什么使用正则表达式解析HTML不是一个好主意的原因。您可以使用正则表达式来排除现有链接,但这很快就会让人头疼。请改用DOM操作。

您可以将正则表达式更改为:

/s(?!href=")#(w+.?w+)/g

它利用了现有链接中的#link没有空格的事实。所以你会得到这样的东西:

function myFunction() {
  var str = document.getElementById("demo").innerHTML;
  var txt = str.replace(/s(?!href=")#(S+)/g, "<a href="http://example.com?hashtag=$1"> #$1</a> ");
  document.getElementById("demo").innerHTML = txt;
}
<button onclick="myFunction()">Try it</button>
<p id="demo">Please visit #Microsoft! #facebook
  <a href="#link"> Somelink</a>
</p>

最新更新