50
votes

I want to find a string of text in an element and wrap some span tags round it. E.g.

From:

<h2>We have cows on our farm</h2>

To:

<h2>We have <span class='smallcaps'>cows</span> on our farm</h2>

I've tried:

$("h2:contains('cow')").each(function() {
 $(this).text().wrap("<span class='smallcaps'></span>");
});

But that only wraps the whole containing h2 tag.

6

6 Answers

83
votes
$("h2:contains('cow')").html(function(_, html) {
   return html.replace(/(cow)/g, '<span class="smallcaps">$1</span>');
});

http://jsfiddle.net/w5ze6/1/

17
votes

Another approach, split by keyword and join with the updated html.

$("h2:contains('cow')").html(function(_, html) {
   return html.split('cow').join("<span class='smallcaps'>cow</span>");
});

Note: I haven't tested this, but I'm assuming this would perform worse than doing a replace, but figure I would include anyways for informative purposes

3
votes

Here is a variation on @undefined's answer, which loops through an array of items:

var barnyardArray = [
    'cow',
    'horse',
    'chicken',
    'hog',
    'goat',
    'goose',
    'duck',
    'llama'
];

$.each(barnyardArray, function (index, value) {
    $("p:contains(" + value + ")").html(function (_, html) {
        var regex = new RegExp(value, 'g');
        return html.replace(regex, '<span class="smallcaps">' + value + '</span>');
    });
});
2
votes

You usually want to match some arbitrary text in a case insensitive way. Sometimes, search queries contain characters that are special regex metacharacters and must be escaped before compiling the pattern.

Use a solution like

var words = ["cows", "c++", "$var$"];
$("h2").html(function(_, html) {
   return html.replace(new RegExp(words.map(function(x) { 
       return x.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
     }).join('|'), "gi"), '<span class="smallcaps">$&</span>')
});
span { color: red}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2>We have cows on our farm</h2>
<h2>C++ Apps $var$</h2>

Note that here, a regex will look like /cows|c\+\+|\$var\$/gi, where + and $, special regex metacharacters, are escaped, and the alternatives are ORed with the | alternation operator.

Also, note that there is no need of any outer parentheses as the default group is Group 0 that holds the whole match, and the backreference to Group 0 in JavaScript regex is $&. Thus, the replacement is always done with the search word in the exact case as it was found in text.

Note that in case you need to match whole words only, you would need special boundaries, either \b (if the search words always consist of letters, digits or _) or (\W|^) / (?!\w) (if there can be special characters):

var words = ["cows", "c++", "$var$"];
$("h2").html(function(_, html) {
   return html.replace(new RegExp("(\\W|^)(" + words.map(function(x) { 
       return x.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
     }).join('|') + ")(?!\\w)", "gi"), '$1<span class="smallcaps">$2</span>')
});
span { color: red}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2>We have cows in our farm cowshed</h2>
<h2>C++ Apps $var$ in $var$moretext</h2>
1
votes

In my case i have some tag inside the target div and some text i need to wrap that text into a link.

This how i did following "What have you tried".

var oldText = $(this).text(),
newText = $(this).html().replace(
    oldText, 
    "<a class='k-link' href='#' class='smallcaps'>" + 
        oldText + 
    "<span class='k-icon k-i-arrow-n'></span></a>"
);
$(this).html(newText);
-1
votes
$("h2:contains('cow')").each(function() {
    var newText = $(this).html().replace("cow", "<span class='smallcaps'></span>");
    $(this).html(newText);
});