我想使用jQuery动态地将与给定regexp匹配的所有文本与特定标记一起包含在内。例如:
<div class="content">
<div class="spamcontainer">
Eggs and spam is better than ham and spam.
<img src="spam-and-eggs.jpg">
I do not like green eggs and spam, though.
</div>
</div>
如果我的正则表达式是ham|spam
而我想用<span class='meat-product'>
括起来,那么我想转换为
<div class="content">
<div class="spamcontainer">
Eggs and <span class='meat-product'>spam</span> is better than <span class='meat-product'>ham</span> and <span class='meat-product'>spam</span>.
<img src="spam-and-eggs.jpg">
I do not like green eggs and <span class='meat-product'>spam</span>, though.
</div>
</div>
这是我的问题。我知道如何只使用文本,只使用html:
$("div.content").text(function() {
return $(this).text().replace(/ham|spam/, function(match) {
return '<span class="meat-product">' + match + '</span>';
});
});
和
$("div.content").html(function(_, html) {
return html.replace(/ham|spam/, function(match) {
return '<span class="meat-product">' + match + '</span>';
});
});
但前者用文本替换文本(因此我得到文本<span ...
而不是<span>
元素),后者匹配任何HTML中的ham
和spam
而不仅仅是文本。
如何仅匹配文本,还能用HTML元素替换该文本?
答案 0 :(得分:2)
如何仅匹配文本,还能用HTML元素替换该文本?
您可以选择所有后代元素和文本节点,并将该集过滤为仅包含文本节点。然后,您只需使用修改后的字符串替换每个文本节点:
$(".content *").contents().filter(function() {
return this.nodeType === 3;
}).replaceWith(function () {
return this.textContent.replace(/(ham|spam)/g, '<span class="meat-product">$1</span>');
});
$(".content *").contents().filter(function() {
return this.nodeType === 3;
}).replaceWith(function() {
return this.textContent.replace(/(ham|spam)/g, '<span class="meat-product">$1</span>');
});
&#13;
.meat-product {
color: #f00;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="content">
<div class="spamcontainer">
Eggs and spam is better than ham and spam.
<img src="spam-and-eggs.jpg">I do not like green eggs and spam, though.
</div>
</div>
&#13;
*
来选择所有后代元素(当然你也可以使用$('.content').children()
,但这只会深入一级......你可能会遇到嵌套元素表示您需要检索所有文本节点,因此我使用*
选择所有后代元素的内容。).contents()
method获取所有文本节点和子元素。nodeType
属性值为3。.replaceWith()
method用于使用替换的文本字符串更新每个文本节点。 .replaceWith()
方法的好处是可以用HTML字符串替换文本。.replace()
方法。然后,您只需在$1
元素之间替换span
。答案 1 :(得分:1)
这是一个解决方案,涵盖了所显示的简单案例,仅用于展示如何使用jQuery遍历文本节点。如果你必须处理更深的嵌套
,它将需要一些递归的DOM行走var reg = /ham|spam/g;
$('.content').children().contents().each(function() {
if (this.nodeType === 3) {
var txt = this.textContent;
if (txt.match(reg)) {
$(this).replaceWith(textToHtml(this));
}
}
});
function textToHtml(node) {
return node.textContent.replace(reg, function(match) {
return '<span class="meat-product">' + match + '</span>';
})
}
正如评论中所提到的,有战斗测试的插件可以为你做这个
的 DEMO 强>
答案 2 :(得分:1)
我最终使用了Josh Crozier的解决方案。问题是如果文本中有& < >
个字符,如果它们被用作replaceWith()
的HTML参数,则需要对它们进行转义。如果您有意插入HTML,它们将无法被普遍转义。所以我做了这样的事情:
function escapeHTML(s) {
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
}
$("div.content *").contents().filter(function() {
return this.nodeType === 3;
}).each(function() {
var node = $(this);
var text = node.text();
var occurrences = 0;
var html = text.replace(/(ham|spam)|.+?/g, function(match, p1) {
if (p1)
{
// we found ham or spam
++occurrences;
return '<span class="meat-product">'
+ escapeHTML(match) // technically not necessary
// since we know "ham" and "spam"
// don't need escaping, but some regex's
// need this
+ '</span>';
}
else
{
return escapeHTML(match);
}
});
if (occurrences > 0)
{
node.replaceWith(html);
}
});
答案 3 :(得分:1)
我很欣赏OP明确指出
我想使用
jQuery
但我想看看我是否可以构建普通的javascript
解决方案。 (我还没有开始学习jQuery
)。
最后,我的解决方案涉及3个步骤:
childNodes
;
/* ### Build an array of 'matching elements' which have (as direct childNodes) text nodes containing 'spam' or 'ham' */
var matchingElements = [];
var allElements = document.querySelectorAll('*:not(script)');
for (var i = 0; i < allElements.length; i++) {
var elementAdded = false;
for (var j = 0; j < allElements[i].childNodes.length; j++) {
if (allElements[i].childNodes[j].nodeValue === null) {continue;}
if (allElements[i].childNodes[j].nodeValue.match(/ham|spam/gi) === null) {continue;}
if (elementAdded !== true) {
matchingElements.push(allElements[i]);
elementAdded = true;
}
}
}
/* ### Clone each matched element and rebuild it, replacing unstructured text nodes */
var x;
var y;
var newTextNode;
var clonedElements = [];
var meatProducts = document.getElementsByClassName('meat-product');
/* Create (empty) clone of matched element */
for (var k = 0; k < matchingElements.length; k++) {
clonedElements[k] = matchingElements[k].cloneNode(false);
matchingElements[k].parentNode.insertBefore(clonedElements[k],matchingElements[k]);
for (var l = 0; l < matchingElements[k].childNodes.length; l++) {
/* Add non-text nodes to cloned element */
if (matchingElements[k].childNodes[l].nodeValue === null) {
var clonedNode = matchingElements[k].childNodes[l].cloneNode(true);
clonedElements[k].appendChild(clonedNode);
continue;
}
/* Add text nodes which don't contain 'spam' or 'ham' to cloned element */
if (matchingElements[k].childNodes[l].nodeValue.match(/ham|spam/gi) === null) {
var clonedNode = matchingElements[k].childNodes[l].cloneNode(true);
clonedElements[k].appendChild(clonedNode);
continue;
}
/* Restructure and add text nodes which do contain 'spam' or 'ham' to cloned element */
var meatLarder = matchingElements[k].childNodes[l].nodeValue.match(/ham|spam/gi);
var textNodeString = matchingElements[k].childNodes[l].nodeValue;
for (m = 0; m < meatLarder.length; m++) {
x = 0;
y = textNodeString.indexOf(meatLarder[m]);
newTextNode = document.createTextNode(textNodeString.substring(x,y));
clonedElements[k].appendChild(newTextNode);
textNodeString = textNodeString.substring(y);
y = meatLarder[m].length;
var newSpan = document.createElement('span');
var newMeatProduct = document.createTextNode(meatLarder[m]);
var newClass = document.createAttribute('class');
newClass.value = 'meat-product';
newSpan.setAttributeNode(newClass);
newSpan.appendChild(newMeatProduct);
clonedElements[k].appendChild(newSpan);
textNodeString = textNodeString.substring(y);
}
newTextNode = document.createTextNode(textNodeString);
clonedElements[k].appendChild(newTextNode);
}
/* Remove original matched element from document, leaving only the newly-built cloned element */
matchingElements[k].parentElement.removeChild(matchingElements[k]);
}
<div class="content">
<div class="spamcontainer">
Eggs and spam is better than ham and spam.
<img src="spam-and-eggs.jpg">
I do not like green eggs and spam, though.
</div>
</div>