0

EDIT

heres what i have to do...

Imagine if i have a text with some html tags inside it (it is still a string):

var string = '<p>Hello, my name is Mauricio</p><p>Hi, my name is Patricia</p><p class="warn">Yeah, My name is Carl</p><a href="#"><img src="#" /></a>';

And i want to wrap all the letters "a" with

 <span class="ui-match"></span>

but i must not replace anything from the tag, neither what is inside it, neither the class in the

element.

So if I want to wrap all the letters "a" from that string, it would return like that:

<p>Hello, my n<span class="ui-match">a</span>me is M<span class="ui-match">a</span>uricio</p><p>Hi, my n<span class="ui-match">a</span>me is P<span class="ui-match">a</span>trici<span class="ui-match">a</span></p><p class="warn">Ye<span class="ui-match">a</span>h, My n<span class="ui-match">a</span>me is C<span class="ui-match">a</span>rl</p><a href="#"><img src="#" /></a>

all the letters "a" where wrapped with

 <span class="ui-match"></span>

, but the link and the paragraph were not.

also this string is comming from a API, so its dynamic... this letter i'm searching is dynamic, so it can be "a" or "abc"... it must not be case sensitive

thanks

2
  • Are you only able to use regex, or can you use regex in combination with other functions? Commented Dec 19, 2013 at 18:43
  • 1
    You can combine everything... :) Commented Dec 19, 2013 at 18:47

3 Answers 3

2

Does this solution matches your requirements?

string = string.replace(/a(?![^<]*?>)/g, '<span class="ui-match">a</span>');

A little help about (?![^<]*?>) (roughly : "some text not followed by >") :

(?!...)   not followed by
[^<]*     any char except "<", zero or more times
?>        until next ">"

Wrapped inside a function :

function replace(html, text, replacement) {
    // RegExp.escape : http://stackoverflow.com/q/3561493/1636522
    var re = new RegExp('(' + RegExp.escape(text) + ')(?![^<]*?>)', 'g');
    return html.replace(re, replacement);
}
var html = '<a class="azerty"> azerty &lt; azerty </a>';
html = replace(html, 'azerty', '<b>$1</b>');
// "<a class="azerty"> <b>azerty</b> &lt; <b>azerty</b> </a>"
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your answer @wared... but the answer from Miller Meideiros seem to be simpler, and is working great. but i really appreciate your help. Thanks!
@MauricioSoares You're welcome :D Be careful though, your choice fails the same way.
@MauricioSoares Forget what I've told you previously, actually there can't be any < and > between tags since we have to replace them with HTML entities, respectively &lt; and &gt;. So, obviously, "your choice fails the same way"... I have to review my answer entirely ^^' Stupid me.
@MauricioSoares I'm reverted back to the first revision :D
@MauricioSoares And added a function for convenience :)
1

I would recommend you to split the problem into 2 smaller problems:

  1. grab text content of all tags.
  2. wrap chars with <span class="ui-match"></span>

Using RegExp to parse HTML is a bad idea but in this case since you seem to control the input structure you might use it to simplify the logic.

Using a single RegExp for it will be really hard, so it's also better to do 2 String#replace instead of one. A generic implementation would be like:

function replaceHtmlContent(str, match, replaceFn) {
  // we use the "g" and "i" flags to make it replace all occurrences and ignore case
  var re = new RegExp(match, 'gi');
  // this RegExp will match any char sequence that doesn't contain "<" or ">"
  // and that is followed by a tag
  return str.replace(/([^<>]+)(?=<[^>]+>)/g, function(s, content){
    return content.replace(re, replaceFn);
  });
}

which could be abstracted as:

function wrapMatch(src, match) {
  return replaceHtmlContent(src, match, function(str){
    return '<span class="ui-match">'+ str +'</span>';
  });
}

and used later like:

var output = wrapMatch(input, 'a');

which would give the expected result for the example input.

DEMO: http://jsbin.com/ovUFEsas/4/edit

Comments

1

For not using regex, it will be faster to work with DOM nodes:

var div = document.createElement('div'),
    children;

div.innerHTML = 'Hello, my name is mauricio, and i like <a href="#">Star Wars</a>';
children = div.childNodes;

for (var i = 0, len = children.length; i < len; i++) {
    console.log(children[i]);
    if (children[i].nodeType === 3) {
        children[i].nodeValue = children[i].nodeValue.replace(/a/g, 'R');
    }
}

console.log(div.innerHTML);

N.B.: I used innerHTML property as an example way here, however it is not recommended to exploit it because of a rather low performance.

DEMO: http://jsfiddle.net/N7rdW/


UPDATE:

As per your update, you should better use the approach from my answer for another question from HERE. The code is a bit more complicated but is rather fast (not keeping in mind innerHTML usage):

var div = document.createElement('div');
div.innerHTML = 'Hello, my name is mauricio, and i like <a href="#">Star Wars</a>';

for (var i = 0, children = div.childNodes, len = children.length; i < len; i++) {
    var child = children[i];
    if (child.nodeType === 3 && child.nodeValue.indexOf('a') > -1) {
        var segments = child.nodeValue.split('a');
        for (var k = 0, lk = segments.length; k < lk; k++) {
            div.insertBefore(document.createTextNode(segments[k]), child);
            if (k < lk - 1) {
                var span = document.createElement('span');
                span.className = 'ui-match';
                span.appendChild(document.createTextNode('R'));
                div.insertBefore(span, child);
            }
        }
        div.removeChild(child);
    }
}

console.log(div.innerHTML);

DEMO: http://jsfiddle.net/T4ZXA/6/

6 Comments

Thanks Vision! i'm sorry but i formulated my question wrong... i just updated it... i dont want to replace, i want to wrap with a tag
@MauricioSoares Then you should better use my other answer for the similar question from here: stackoverflow.com/a/16239900/1249581.
hey @VisioN, do you mind creating an example that works with my string? I don't think i got your answer really well... also, is there a way to do this without creating an element? only manipulating the string?
@MauricioSoares Please see my updated answer. FYI, you should never try to parse HTML with regex.
Thanks, i'm testing your answer... :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.