我试图在HTML字符串中找到给定的单词并在其周围添加一个范围。
我现在正在做的是:
function find(what:String,where:String)
{
var regexp:RegExp=new RegExp(what,'gi');
return where.replace(regexp,'<span>$&</span>');
}
它适用于不在HTML标记内的单词。 我想要的是忽略HTML标签内的那些。
示例:查找(“西班牙”)
输入:
The rain in <b class="spain">Spain</b> stays mainly in the <i data-test="Spain">plain</i>.
输出:
The rain in <b class="spain"><span>Spain</span></b> stays mainly in the <i data-test="Spain">plain</i>.
我怎么能实现这个目标呢?
答案 0 :(得分:2)
您将无法使用正则表达式以任何可靠的方式处理HTML。相反,将HTML解析为DOM树并迭代Text节点,检查其数据是否有内容。
如果您在Web浏览器中使用JavaScript,则已经为您完成了解析。请参阅this question,例如wrap-word-in-span代码。如果你需要匹配可能分散在不同元素中的短语,那就太麻烦了。
答案 1 :(得分:2)
要考虑可以匹配的html标记和属性,您需要以这种或那种方式解析该HTML。最简单的方法是将其添加到DOM(或仅添加到新元素):
var container = document.createElement("div");
container.style.display = "none";
document.body.appendChild(container); // this step is optional
container.innerHTML = where;
解析后,您现在可以使用DOM方法迭代节点,只查找文本节点并搜索这些节点。使用递归函数来遍历节点:
function wrapWord(el, word)
{
var expr = new RegExp(word, "i");
var nodes = [].slice.call(el.childNodes, 0);
for (var i = 0; i < nodes.length; i++)
{
var node = nodes[i];
if (node.nodeType == 3) // textNode
{
var matches = node.nodeValue.match(expr);
if (matches)
{
var parts = node.nodeValue.split(expr);
for (var n = 0; n < parts.length; n++)
{
if (n)
{
var span = el.insertBefore(document.createElement("span"), node);
span.appendChild(document.createTextNode(matches[n - 1]));
}
if (parts[n])
{
el.insertBefore(document.createTextNode(parts[n]), node);
}
}
el.removeChild(node);
}
}
else
{
wrapWord(node, word);
}
}
}
这是一个有效的演示:http://jsfiddle.net/gilly3/J8JJm/3
答案 2 :(得分:1)
function find(what:String,where:String)
{
what = what.replace(/(\[|\\|\^|\$|\.|\||\?|\*|\+|\(|\)|\{|\})/g, "\\$1")
.replace(/[^a-zA-Z0-9\s:;'"~[\]\{\}\-_+=(),.<>*\/!@#$%^&|\\?]/g, "(?:&[0-9A-Za-z]{3,25};|&#[0-9]{1,10};?|[^\s<])")
.replace(/</g,"<?").replace(/>/g,">?").replace(/"/g,"(?:\"|"?)")
.replace(/\s/g, "(?:\\s| ?)");
what = "(>[^<]*|^[^<]*)(" + what + ")";
var regexp:RegExp=new RegExp(what,'gi');
return where.replace(regexp,'$1<span>$2</span>');
}
(?:&[0-9A-Za-z]{3,25};|&#[0-9]{1,10};?|[^\s<])
替换搜索查询中每次出现的未知字符。此RE由三部分组成:首先,它尝试匹配HTML实体。其次,它尝试匹配HTML数字实体。最后,它匹配任何非空白字符(如果HTML文档的创建者没有正确编码字符)。<
,>
和"
,这样搜索查询就不会搜索标记。\s| ?
)替换空格,它匹配空格字符和HTML实体。此功能的唯一缺点是未记录的特殊字符(例如€
)匹配任何HTML实体/字符(遵循示例,不仅&euro
;而€
有效匹配,还有£
和@
)。
此建议的解决方案适合大多数情况。在复杂的情况下它可能是不准确的,这可能不比DOM迭代更糟糕(它非常容易受到内存泄漏的影响并且需要更多的计算能力)。
当您处理通过DOM分配了事件侦听器的HTML元素时,您应该遍历所有(子)元素,并将此函数应用于每个Text节点。
答案 3 :(得分:0)
纯JavaScript(基于jQuery的Sizzle.getText); Demo: http://jsfiddle.net/vol7ron/U8LLv/
var wrapText = function ( elems,regex ) {
var re = new RegExp(regex);
var elem;
for ( var i = 0; elems[i]; i++ ) {
elem = elems[i];
// Get the text from text nodes and CDATA nodes
if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
parent = elem.parentNode;
re.lastIndex = 0;
if(re.test(elem.nodeValue)){
var span = document.createElement('span');
span.innerHTML = RegExp.$1;
if (RegExp.leftContext != ''){
parent.insertBefore(document.createTextNode(RegExp.leftContext),elem); i++;
}
parent.insertBefore(span,elem); i++;
if (RegExp.rightContext != ''){
parent.insertBefore(document.createTextNode(RegExp.rightContext),elem); i++;
}
parent.removeChild(elem);
}
// Traverse everything else, except comment nodes
} else if ( elem.nodeType !== 8 ) {
wrapText( elem.childNodes, regex );
}
}
return;
};
var obj = document.getElementById('wrapper');
wrapText([obj],/(spain)/gi);