简体   繁体   中英

How to replace string without losing text format already done to a part of text in content editable div

I have a div whose conenteditble property set to true.

Assume, user has typed something like

The :lazy brown fox jumps over a super :lazy dog

Now, I would like to replace second ":lazy" appears in the sentence. This is dynamic. The sentence may contain any number of ":lazy" (or "any word" which must be replaced with "some text") and only required one has to be replaced. This has to be done without losing text format already done to the sentence.
Here is what I tried. This will always replace first occurrence of the word.

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
</script>
<script>
      $(document).ready(function(){
      $("#rewrite").click(function(){
      var regex=/\.*[:]\S*/
      var originalHtml = $('#editableDiv').html();
      var newHtml = originalHtml.replace(regex,"sleeping");
      $('#editableDiv').html(newHtml);
      });
    });
</script>
</head>
<body>
<form name="myform">
<div id="editableDiv"  contenteditable="true">
    the quick <b><i>:lazy</i></b> <b><i>brown</i></b> fox jumps over a <b><i>:lazy<span style="color:red;"> dog </span></i></b>dog
</div>
<input type="button" id="rewrite" value="Rewrite">
</form>
</body>
</html> 

Assuming the regex variable is set as such:

//                               v - make the search global
var regex = /\.*[:]\w+(?!\w*[;])/g
//Only word letters ^^ ^- negative look-ahead; make sure that the characters
//one or more matches|    do not have a semi-colon following them(like inline
//                        css styles).

It should not modify any surrounding tags. Granted, I haven't given this all test cases, because I couldn't think of many, but it worked here with what I gave it.

As you likely already know, the \\S character in regex is a negated space character, meaning its much like the dot(.), in that it can pretty much match any character. This includes brackets( <> ), which would cause it to match html, and thus break the html string you have.

Edit:

Due to the fact that javascript doesn't have negative look-behinds, what I came up with as an answer is kind of a hack, in my opinion.. but it works and is configurable:

Replace your call to the replace method with this:

var regex = /\.*([:]\w+(?!\w*[;]))/g;//A change here to make it match group
var originalHtml = $('#editableDiv').html(); // Didn't change anything here, just
                                             // included it to not break anything
var mi = 1; //match index
var newHtml = originalHtml.replace(regex, function() {
    var ret = (mi == 2)?'sleeping':arguments[0];
    //         boolean   if true    if false
    // The above is a ternary operator in case you weren't aware.  arguments is
    // an array composed of all arguments sent to the anonymous function, with
    // the first being the match, then the match groups, then the position in
    // the string in which is is found, and finally the full string it was
    // matched against.

    // The above if/then/else operator checks if the match index(mi) is equal to
    // the position of 2, if it is, then return 'sleeping', if not, return the 
    // match word

    mi++; //increase match index by one, every time function is ran
          //as it is ran each time there is a grouped match
    return ret; //return return variable
});

Here is a demo demonstrating the above.

If by any chance you need to replace only the second one, you can use this code:

var re = /((?:\<[^<>]+\>\s?)?)([:]\w+(?!\w*[;]))((?:\s?\<\/[^<>]+\>)?)((?:(?:.(?!\1\2\3))*.){1})\1\2\3/;

var str = 'Lorem ipsum dolor <b>:auctor</b> sit amet, consectetur <b>:auctor</b> adipiscing elit. Suspendisse eget enim posuere mi cursus hendrerit. <b>:auctor</b> Integer id mauris ipsum. In elementum sapien <b>:auctor</b> eget sem porttitor eu ultricies mauris <b>:auctor</b>.';

var replacing = "replaced here!!";

response = str.replace(re,"$1$2$3$4$1" + replacing + "$3");

document.write('<pre>');
document.write(response);

In the code we replaced only the second match of the :auctor occurrence (without loosing the text format tags)

Also, if you want to only replace the third one, just increase the statement {1} to {2} , making the expression be like this /((?:\\<[^<>]+\\>\\s?)?)([:]\\w+(?!\\w*[;]))((?:\\s?\\<\\/[^<>]+\\>)?)((?:(?:.(?!\\1\\2\\3))*.){2})\\1\\2\\3/ .

(following the same reasoning, if you want the fourth, increase to {3} and so on...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM