TL; DR;摘要
如何注入< span>围绕当前页面的HTML中找到的特定单词或短语但忽略我想要注入的同一范围内包含 ALREADY 的任何文字。
由于处理了大量值,因此必须具有高性能!
示例:
搜索“foo”
应找到匹配项:
< p>这句话包含一个foo bar值< / p>
不应该找到匹配项:
< p>这句话包含< span class ='widget'> foo bar< / span>值LT; / p为H.
背景 - 即为什么?
我正在研究一个特定的问题,即必须注入一个< span class ='widget'>动态地在页面上找到的特定文本周围的元素。我正在寻找的文字是一个很大的数组。
这最后一个是杀手。 例如:
我完成处理后......
所需输出
“这是一个< span class ='widget'> foo bar< / span>句子”
不想要
“这是一个< span class ='widget'> foo< span class ='widget'> bar< / span>< / span>句子”
现在......实现这一目标的第一步是按长度对数组进行排序(首先处理最长的数组)。但问题是,在处理完后,我的find-replace逻辑仍然在(已处理的)短语中找到较小的“单词”。
答案 0 :(得分:1)
当且仅当没有嵌套的<span>
- 标签时,您可以搜索
/(<span\b[^>]*>[\s\S]*?<\/span>)|(\b(?:foo|bar)(?:\s+(?:foo|bar))*)/g
并将其替换为函数
function matchEvaluator(_, span, word) {
if (span) return span;
return '<span class="widget">' + word + '</span>';
}
(<span\b[^>]*>[\s\S]*?<\/span>)
搜索span元素
那是不允许嵌套<span>
- 元素的部分。匹配的文本返回不变(匹配它们的原因是消耗<span>
内的所有字符)<span\b[^>]*>
搜索开始标记 - 这可能不足以满足您的需求。也许你会尝试更具体,例如像<span\b(?:\s+\w[\w-]*(?:=(?:"[^"]*"|'[^']*'|\S*)))*>
(\b(?:foo|bar)(?:\s+(?:foo|bar))*)
搜索单词&#34; foo&#34;和&#34; bar&#34; <span>
- 标签及其所有内容已被消费,因此您只能匹配&#34; foo&#34;和&#34; bar&#34;在<span>
测试:
var texts = [
"This is a foo bar sentence",
"This sentence contains a <span class='widget'>foo bar</span> value"
];
var wordsOutsideSpan_rx = /(<span\b[^>]*>[\s\S]*?<\/span>)|(\b(?:foo|bar)(?:\s+(?:foo|bar))*)/g;
function wrapInSpan(_, span, word) {
if (span) return span;
return '<span class="widget">' + word + '</span>';
}
texts.forEach(function (txt) {
console.log(txt.replace(wordsOutsideSpan_rx, wrapInSpan));
});
// outputs
// "This is a <span class="widget">foo bar</span> sentence"
// "This sentence contains a <span class='widget'>foo bar</span> value"
答案 1 :(得分:0)
好的,这是另一种方式。
我使用jQuery来查找元素(不是真的需要,但它很方便)。
此解决方案接受嵌套的<span>
,并且可能更快。请分享您的结果。
(function () {
var testwords_rx = /\b(?:foo|bar)\b/; // it's annoying, but should be faster
var words_rx = /\b(?:foo|bar)\b(?:\s+(?:foo|bar)\b)?/g;
function filterTextElement(idx, element) {
return element != null &&
element.nodeType == 3 && // #text node
element.nodeValue.match(testwords_rx); // find at least one match
}
function wrapFoobars(idx, element) {
var lastPos = 0;
var text = element.nodeValue;
var parent = element.parentNode;
function addUnwrapped(start, end) {
var textNode = document.createTextNode(text.substring(start, end));
parent.insertBefore(textNode, element);
}
function addWrapped(start, end) {
var span = document.createElement('span');
span.className = 'widget';
span.style.border = "1px solid red";
var txtprop = 'textContent' in span ? 'textContent' : 'innerText';
span[txtprop] = text.substring(start, end);
parent.insertBefore(span, element);
}
function splitAndWrapText(words, pos) {
if (pos > lastPos) {
addUnwrapped(lastPos, pos);
}
lastPos = pos + words.length;
addWrapped(pos, lastPos);
}
text.replace(words_rx, splitAndWrapText);
if (lastPos < text.length) {
addUnwrapped(lastPos, text.length);
}
parent.removeChild(element);
}
$('body *')
.filter(':not(.widget, .widget *)')
.contents()
.filter(filterTextElement)
.each(wrapFoobars)
;
})();
$('body *').filter(':not(.widget, .widget *)')
选择<body>
中的所有代码并过滤掉.widget
- 元素及其所有后代(将其更改为仅选择您需要的元素)
.contents()
获取匹配元素的所有子元素(包括文本节点)
.filter(filterTextElement)
过滤以仅获取至少包含其中一个搜索字词的#text元素
wrapFoobars
:
替换比赛。必须将第一个之间,最后一个匹配之间和之后的文本作为文本节点(addUnwrapped
)插入,匹配的文本本身将包装到新创建的<span>
- 元素(addWrapped
)。
最后,删除原始文本元素(parent.removeChild(element);
)