JavaScript 正则表达式 #hasgtag 替换成 link 没有超级哈希 link in html
JavaScript RegExp #hasgtag replace into link without hyper hashlink in html
我想用 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="<a href=" http:="" example.com?hashtag="selectedteg"">#Selected</a> "> 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>
您需要捕获 组,然后在替换中使用它。类似于:
var txt = str.replace(/#(\w+\.?\w+)/g,"<a href=\"http://example.com?hashtag=\">#</a> ");
将要捕获的部分放在方括号中使其成为捕获组,然后捕获的组将插入替换字符串中的 </code> 标记处。</p>
<p>当然,你更大的问题是你的正则表达式匹配你现有的 link 并试图在其中进行替换,这完全把事情搞砸了。这就是为什么使用正则表达式来解析 HTML 不是一个好主意。您可以处理正则表达式以排除现有的 link,但这很快就会让人头疼。请改用 DOM 操作。</p>
<p>您可以将正则表达式更改为:</p>
<pre><code>/\s(?!href=")#(\w+\.?\w+)/g
它利用了这样一个事实,即您现有的 link 中的 #link
不是 space。所以你得到这样的东西:
function myFunction() {
var str = document.getElementById("demo").innerHTML;
var txt = str.replace(/\s(?!href=")#(\S+)/g, "<a href=\"http://example.com?hashtag=\"> #</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>
您必须捕获带括号的文本,但也必须只捕获文本,而不是 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
var hashtagsReplaced= (start+capture+end).replace(/\#(\w+)/g,"<a href=\"http://example.com?hashtag=\">#</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>
哇!这是一个出乎意料的难题,虽然乍一看应该很简单。
问题在于,严格来说,您的需求要求 仅处理文本节点 以将主题标签转换为链接。现有 HTML 根本 不应该 被触及。
一种天真的方法(在其他答案中看到)会尝试设计一个复杂的正则表达式来躲避 HTML。尽管这似乎适用于某些情况,甚至几乎适用于所有实际情况,但它绝对并非 万无一失。正则表达式根本不够强大,无法完全解析 HTML;它是一种太复杂的语言。在 RegEx match open tags except XHTML self-contained tags 上查看出色且相当著名的 Stack Overflow 答案。它不可能完美地完成,而且根本不应该完成。
相反,正确的做法是使用递归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=">#</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()
尽可能编写通用代码总是好的,以最大限度地提高可重用性和通常的简单性(尽管过于通用会导致过度复杂;这是一个折衷)。我写 processByNodeType()
是一个非常通用的函数,它使用 jQuery 遍历 HTML 节点树的子树,从给定的顶部节点开始并向下移动。该函数的目的是做一件事并且只做一件事:为遍历期间遇到的所有节点调用给定的 callback()
函数,这些节点 nodeType
等于 [= 中给出的白名单值之一16=]。这就是为什么我在代码的顶部包含了节点类型常量的枚举;参见 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=">#</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 match open tags except XHTML self-contained tags
我想用 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="<a href=" http:="" example.com?hashtag="selectedteg"">#Selected</a> "> 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>
您需要捕获 组,然后在替换中使用它。类似于:
var txt = str.replace(/#(\w+\.?\w+)/g,"<a href=\"http://example.com?hashtag=\">#</a> ");
将要捕获的部分放在方括号中使其成为捕获组,然后捕获的组将插入替换字符串中的 </code> 标记处。</p>
<p>当然,你更大的问题是你的正则表达式匹配你现有的 link 并试图在其中进行替换,这完全把事情搞砸了。这就是为什么使用正则表达式来解析 HTML 不是一个好主意。您可以处理正则表达式以排除现有的 link,但这很快就会让人头疼。请改用 DOM 操作。</p>
<p>您可以将正则表达式更改为:</p>
<pre><code>/\s(?!href=")#(\w+\.?\w+)/g
它利用了这样一个事实,即您现有的 link 中的 #link
不是 space。所以你得到这样的东西:
function myFunction() {
var str = document.getElementById("demo").innerHTML;
var txt = str.replace(/\s(?!href=")#(\S+)/g, "<a href=\"http://example.com?hashtag=\"> #</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>
您必须捕获带括号的文本,但也必须只捕获文本,而不是 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
var hashtagsReplaced= (start+capture+end).replace(/\#(\w+)/g,"<a href=\"http://example.com?hashtag=\">#</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>
哇!这是一个出乎意料的难题,虽然乍一看应该很简单。
问题在于,严格来说,您的需求要求 仅处理文本节点 以将主题标签转换为链接。现有 HTML 根本 不应该 被触及。
一种天真的方法(在其他答案中看到)会尝试设计一个复杂的正则表达式来躲避 HTML。尽管这似乎适用于某些情况,甚至几乎适用于所有实际情况,但它绝对并非 万无一失。正则表达式根本不够强大,无法完全解析 HTML;它是一种太复杂的语言。在 RegEx match open tags except XHTML self-contained tags 上查看出色且相当著名的 Stack Overflow 答案。它不可能完美地完成,而且根本不应该完成。
相反,正确的做法是使用递归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=">#</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()
尽可能编写通用代码总是好的,以最大限度地提高可重用性和通常的简单性(尽管过于通用会导致过度复杂;这是一个折衷)。我写 processByNodeType()
是一个非常通用的函数,它使用 jQuery 遍历 HTML 节点树的子树,从给定的顶部节点开始并向下移动。该函数的目的是做一件事并且只做一件事:为遍历期间遇到的所有节点调用给定的 callback()
函数,这些节点 nodeType
等于 [= 中给出的白名单值之一16=]。这就是为什么我在代码的顶部包含了节点类型常量的枚举;参见 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=">#</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 match open tags except XHTML self-contained tags