31

What I want to do is use jQuery to find a piece of text within a paragraph and add some CSS to make it bold. I can't seem to figure out why this won't work:

$(window).load(function() {
// ADD BOLD ELEMENTS
$('#about_theresidency:contains("cross genre")').css({'font-weight':'bold'});
});

The text in "about_theresidency" is dynamically pulled in, hence me executing it after the window has loaded.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Michael
  • 807
  • 4
  • 14
  • 25
  • 2
    Even if this will work somehow out, it will make the full element bold, but surely not only your two words. CSS can only be applied to tags, not to text – Sven Bieder Mar 20 '12 at 21:04
  • @Diodeus i'm using JSON to drop the text into the span tag: `

    ABOUT US

    `
    – Michael Mar 20 '12 at 21:05
  • I also tried: `$('#about_theresidency:contains("cross genre")').wrap("");` But to no avail – Michael Mar 20 '12 at 21:08
  • even being able to add a class to the text would be suitable, just can't figure out how to select that bit of text. – Michael Mar 20 '12 at 21:12
  • does not contain "cross genre" so the selector will fail. – Diodeus - James MacFarlane Mar 20 '12 at 21:12
  • well, not in my original html, but it is then populated with the following text http://pastie.org/3637586 ... I should have said that before, sorry! – Michael Mar 20 '12 at 21:17
  • If this code is in $(window).load(), where is the code that pulls in the json string and sets the contents? I'm guessing afterwards, which is why this isn't working. $(window).load() is pretty early. – bhamlin Mar 20 '12 at 21:33
  • the other code is in $(document).ready() but when working code (by @elclanrs) isn't running automatically. I'm going to have to get this code to fire at the very end. – Michael Mar 20 '12 at 21:52

5 Answers5

89

You can use replace() with html():

var html = $('p').html();
$('p').html(html.replace(/world/gi, '<strong>$&</strong>'));

Edit: http://jsbin.com/AvUcElo/1/edit

I turned it into a lil' plugin, here:

$.fn.wrapInTag = function(opts) {

  var tag = opts.tag || 'strong'
    , words = opts.words || []
    , regex = RegExp(words.join('|'), 'gi') // case insensitive
    , replacement = '<'+ tag +'>$&</'+ tag +'>';

  return this.html(function() {
    return $(this).text().replace(regex, replacement);
  });
};

// Usage
$('p').wrapInTag({
  tag: 'em',
  words: ['world', 'red']
});
elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • thanks amigo, thats done the trick, stuck it in my loop and it worked! – Michael Mar 20 '12 at 22:23
  • nice plugin..but what if there are 1000 of different words which are enclosed withing quotes and all those words needs to be given class red ? we might need to add regex expression in 'code' words: [Regex here, 'red'] what wud be the regex ? – Jigar Jain Nov 25 '12 at 09:19
  • I guess you can just pass the words with quotes: `words: ['"world"', '"red"']` – elclanrs Nov 25 '12 at 09:54
  • 3
    Your solution works, but breaks inner tags (if there were any inside the element you alter), so I took the liberty to modify it a bit and post it as an alternative answer. – jahu Feb 17 '14 at 08:57
  • Just as a headsup for anyone else wondering, this fails in IE8 and halts execution. – red Sep 17 '14 at 16:17
16

I had a similar problem and tried to use the solution proposed by elclanrs. It works great, but only if you have no html elements within the text you want to alter. If there were any tags inside the text, they would have been lost after running the wrapInTag function.

Here is my inner-node-friendly solution to the problem (I included links to the resources I used to write the code).

// http://stackoverflow.com/a/9795091
$.fn.wrapInTag = function (opts) {
    // http://stackoverflow.com/a/1646618
    function getText(obj) {
        return obj.textContent ? obj.textContent : obj.innerText;
    }

    var tag = opts.tag || 'strong',
        words = opts.words || [],
        regex = RegExp(words.join('|'), 'gi'),
        replacement = '<' + tag + '>$&</' + tag + '>';

    // http://stackoverflow.com/a/298758
    $(this).contents().each(function () {
        if (this.nodeType === 3) //Node.TEXT_NODE
        {
            // http://stackoverflow.com/a/7698745
            $(this).replaceWith(getText(this).replace(regex, replacement));
        }
        else if (!opts.ignoreChildNodes) {
            $(this).wrapInTag(opts);
        }
    });
};

Example: http://jsbin.com/hawog/1/edit

jahu
  • 5,427
  • 3
  • 37
  • 64
  • Instead of checking for `this.nodeType === 3` you should check `this.nodeType == Node.TEXT_NODE` – Jason Axelson Jan 27 '15 at 20:22
  • 1
    @JasonAxelson As mentioned in http://stackoverflow.com/a/298758, `this.nodeType == Node.TEXT_NODE` will not work in IE7, but `this.nodeType === 3` will work in most browsers. – jahu Jan 28 '15 at 12:35
  • 2
    Thank you! This is awesome. I found that this matches partial words - so if you pass in 'word' it will match on 'word' and 'sword.' I updated this line of code for the RegExp: `regex = RegExp('(^|\\s)' + words.join('(\\s|$)|(^|\\s)') + '(\\s|$)', 'gi'),` This will match on whole words bounded by whitespace only. In my case, I am building a spellchecker, so I needed whole-word highlighting only. – Sean Jun 10 '15 at 19:44
  • @Sean I needed this to highlight search results, so matching partial words was intended. – jahu Jun 11 '15 at 07:48
  • Both are valid use cases, I think :) – Sean Jun 12 '15 at 14:58
  • Why does this not work with a span? Instead of the tag variable I used **replacement = '$&';** and it just does nothing. – olop01 Nov 30 '16 at 13:15
1

Try:

$(window).load(function() {
// ADD BOLD ELEMENTS
    $('#about_theresidency:contains("cross genre")').each(function(){
        $(this).html( 
            $(this).html().replace(/cross genre/g,'<strong>$&</strong>')
        );
    });
});
iambriansreed
  • 21,935
  • 6
  • 63
  • 79
0

Needed a more secure method, that could escape. Here is a vanilla solution

var str = 'The world is big <b onclick="alert(false)">world</b> red. the world is also small'
var words = ['world', 'is']
var reg = RegExp(words.join('|'), 'gi')
var p = document.createElement('p')
p.innerText = str
p.innerHTML = p.innerHTML.replace(reg, '<u>$&</u>')

document.body.appendChild(p)
console.log(p.outerHTML)
Endless
  • 34,080
  • 13
  • 108
  • 131
0

I've taken and adjusted @jahu's provided code into a form where you can add data attributes and then run a callback on the dom to then invoke their $('foo').wrapInTag(...) in and to allow code like this to exist in your template:

<p data-js-highlight-text="hello,">
  <span style="color: #009fff">Hello,</span> World
</p>

// Extended examples with data attributes in your html:
//
// Set the data-js-highlight-text attribute. Before:

<p data-js-highlight-text="hello,">Hello, World</p>

// After:
<p data-js-highlight-text="hello,"><span style="color: #009fff">Hello,</span> World</p>

Nitrodist
  • 1,585
  • 5
  • 24
  • 34