Search code examples
javascriptjqueryreplacehtml-escape-characters

javascript replace method works except when in loop it escapes html


This code works perfectly to replace a string with some html:

$('.active_admin_comment_body').each(function(comment_index){
    the_string = $(this).text().replace("#Excitement", "<span style=\"background-color: lightgrey;\">#Excitement</span>");
     $(this).html(the_string);
)};

The problem with the code above is it only works to replace one value. I have an array of values, so I wrote the following code to loop through the array and then modify each comment with a value from the array:

$('.active_admin_comment_body').each(function(comment_index){
    hashtags = ["#Excitement", "#Sad News", "#Moderate"];
    comment = $(this);
    $.each(hashtags, function(index, value){
        new_comment = comment.text().replace(value, "<span style='background-color: lightgrey;'>" + value + "</span>");
        comment.html(new_comment);
    });
});

The first block of code works 100%. The second block of code only works if you remove the html inside of the replace method. How do I get the second block of code to work?


Solution

  • The culprit is the text() method. When you call it, it only returns the text inside the element, inadvertently undoing any HTML that have already been put in.

    $('.active_admin_comment_body').each(function(comment_index){
        var hashtags = ["#Excitement", "#Sad News", "#Moderate"];
        var comment = $(this);
        $.each(hashtags, function(index, value){
            // I changed text() to html() in the next line.
            var new_comment = comment.html().replace(value, "<span style='background-color: lightgrey;'>" + value + "</span>");
            comment.html(new_comment);
        });
    });
    

    You should also note that replace will only replace the first match. For example, "foo bar foo".replace("foo", "baz") returns "baz bar foo"; you can see that only the first foo is replaced. If you want to replace all instances, you need to specify the g option; "foo bar foo".replace(RegExp("foo", "g"), "baz") returns "baz bar baz". In your code:

    $('.active_admin_comment_body').each(function(comment_index){
        var hashtags = ["#Excitement", "#Sad News", "#Moderate"];
        var comment = $(this);
        $.each(hashtags, function(index, value){
            // I changed text() to html() and added the RegExp constructor.
            var new_comment = comment.html().replace(RegExp(value, "g"), "<span style='background-color: lightgrey;'>" + value + "</span>");
            comment.html(new_comment);
        });
    });
    

    If you change the items inside hashtags to include strings that are not hashtags, you may need to escape certain characters with a \ so that they aren't interpreted as special characters. See this link for a table of characters that need to be escaped. The # sign is not one of the special characters, so if you stick to just hashtags, then you don't need to worry about this.

    Finally, instead of using a loop to change multiple strings, you may want to use a callback replace:

    $('.active_admin_comment_body').each(function(comment_index){
        var hashtags = ["#Excitement", "#Sad News", "#Moderate"];
        var comment = $(this);
        var new_comment = comment.html().replace(/#(Excitement|Sad News|Moderate)/g, function(tag){
            return "<span style='background-color: lightgrey;'>" + tag + "</span>";
        });
        comment.html(new_comment);
    });
    

    You'll also notice that I added the var keyword to your code; this makes your variables local instead of global. This does not change the functionality of your code.