8

I try to find and replace text (using jQuery). Actually I'm trying to add a span element around the text. I should also be able to remove the span again without losing the text inside.

For example, let's say I have the following situation to start with:

<span>This is a span element</span>

And I want to be able to do this dynamically using jQuery/JavaScript:

<span>This is a <span class="marked">span</span> element</span>

I should also be able to remove the span.marked and get back to the first result.

The problem however is that I want to do this on all text inside a container with multiple children and subchildren and so on.

The code I got so far will do the following and that is not what I want:

<<span class="marked">span</span>>This is a <span class="marked">span</<span class="marked">span</span> element</span>

I want to do this for ALL text on the page, not just the first it finds.

EDIT: my code so far for doing this:

var re = new RegExp(word, 'g');
$('.container').html($('.container').html().replace(re, '<span class="marked">' + word + '</span>'));

word is a string with the text to wrap spans around.

Wouter
  • 430
  • 2
  • 5
  • 13

7 Answers7

11

To wrap a search string in html you can use something like this:

$('span:contains("word")').html().replace('word', '<span class="marked">word</span>');

and to remove it, you can use something like this:

$('span:contains("word")').html().replace('<span class="marked">word</span>', 'word');

Yes this is a pretty crude way of doing it but it should work.

EDIT: as pointed out by @user3558931, the word needs to be a variable.

To wrap a search string in html you can use something like this:

$('span:contains("' + str + '")').html().replace(str, '<span class="marked">' + str + '</span>');

and to remove it, you can use something like this:

$('span:contains("' + str + '")').html().replace('<span class="marked">' + str + '</span>', str);

EDIT: A glitch in the above code, see the fixed code in this fiddle: http://jsfiddle.net/thePav/7TNk6/21/

PavKR
  • 1,591
  • 2
  • 11
  • 26
  • 1
    This will not work. Per PO `word` is a variable, not a string. – PeterKA May 20 '14 at 13:16
  • Apologies, I shall update the code, I think the original question was updated after I had answered. – PavKR May 20 '14 at 13:18
  • 1
    This will only work on span tags? I'm trying to do so for all nested tags within a container. I tried your code like $('*:contains(.... but that didn't seem to work. – Wouter May 20 '14 at 13:41
  • Yep, that piece of code is directed at span tags. To do this with selector classes you could use something like $('.container').html() – PavKR May 20 '14 at 13:44
  • Apologies, there was a glitch in that code. First you would have to store the data into a variable and then apply it. Check out this fiddle: http://jsfiddle.net/thePav/7TNk6/21/ – PavKR May 20 '14 at 14:06
  • Just edit the code in the answer; don't make people go to another site to find the answer... – Heretic Monkey Feb 26 '21 at 16:51
6
$('.container p').each(function() {
    var text = $(this).text();
    $(this).html(text.replace('word', '<strong><span class="accordion">word </span></strong>')); 
});
Invincible
  • 1,418
  • 18
  • 23
2

Don't use a regular expression. Use DOM traversal, which will be much more reliable, faster and less destructive (event handlers are not destroyed, for example).

See https://stackoverflow.com/a/10618517/96100 for a simple example.

Alternatively, you could use the following:

http://james.padolsey.com/javascript/replacing-text-in-the-dom-solved/

This doesn't cover removing the spans afterwards but that is a reasonably simple matter. Something like the following (untested):

function replaceWithOwnChildren(el){
    var parent = el.parentNode;
    while (el.hasChildNodes()) {
        parent.insertBefore(el.firstChild, el);
    }
    parent.removeChild(el);
}

// Create a non-live standard array of spans
var spans = [].slice.call(document.getElementsByTagName("span"), 0);
for (var i = 0, len = spans.length; i < len; ++i) {
    if (spans[i].className == "marked") {
        replaceWithOwnChildren(spans[i]);
    }
}

// Glue together any adjacent text nodes
document.normalize();
Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
1

I believe the regular expression needs to be adjusted so it does not pick <span or any other HTML tag for that matter. I would like to suggest the RegExp:

var re = new RegExp('[^<\\/](' + word + ')', 'g');

And the replacement:

$('.container').each(function() {
    $(this).html( $(this).html().replace( re, ' <span class="marked">$1</span>' ) );
});

And can be reversed by using the code (Click button in demo below):

$('button.undo').on( 'click', function() {
    $('.container').find( 'span.marked' ).each(function() {
        $(this).replaceWith( word );
    });
});

JS FIDDLE DEMO

OTHER DEMO

PeterKA
  • 24,158
  • 5
  • 26
  • 48
1
var mark = function(word){
    return function(){
        this.innerHTML = this.innerHTML.replace(word,'' + word + '');
    }
};
$('span').each(mark('span'));
Andrey
  • 161
  • 1
  • 3
  • My HTML is a bit more complex: http://jsfiddle.net/TCvg7/2/ <-- that is just an example, the real version has even more tags and nested tags. – Wouter May 20 '14 at 14:37
0

Try using following:

$('.container').forEach(function(){ 
    $(this).html().... 
})

If that is your problem that is. Otherwise, please clarify, what exactly isn't working as it is supposed to?

Jurijs Kastanovs
  • 685
  • 1
  • 15
  • 35
0
var span = document.getElementsByTagName("span")[0];
span.innerHTML = span.innerHTML.replace(/span/g, "<span class=\"marked\">span</span>");

http://jsfiddle.net/7TNk6/1/

ndugger
  • 7,373
  • 5
  • 31
  • 42
  • It's not only span element, it is the whole page I'm trying to modify. Like this: http://jsfiddle.net/7TNk6/3/ – Wouter May 20 '14 at 13:10
  • Alright, well you seem to have your own solution derived from mine, then. What's wrong about yours? – ndugger May 20 '14 at 13:11
  • It replaces html tags as well. It should only replace text BY html tags with text. – Wouter May 20 '14 at 13:34
  • Try this simple fix: http://jsfiddle.net/7TNk6/17/ All I did was add a space before the word in the replace, and in the html that replaces it. It's not elegant, but it works. – ndugger May 20 '14 at 13:55
  • Will give problems when I need to mark the first word of an element. But it's getting closer to what I need. – Wouter May 20 '14 at 14:20