1

I have this:

$(".forum-threadview-post-text:contains(':P')").html(":P").replaceWith("<img src='http://website.com/images/emotes/tongue.png' />");

It is supposed to take any instances of ':P' and replace it with an emoticon image. However, it takes posts with :P and replaces the entire post with that image. How can I replace only the :P.

2 Answers 2

5

Try

var tImg = "<img src='http://website.com/images/emotes/tongue.png' />";
$(".forum-threadview-post-text:contains(':P')").html(function (_, html) {
     return html.replace(/:P/g , tImg )
});

Demo

Reason why yours doesn't work as expected is because you are replacing the matched element(s) with the image, not the specific content of it. You can use .html( function(index, oldhtml) ) to get the html of each element and replace it.

Or:

$(".forum-threadview-post-text:contains(':P')").contents().each(function () {
    if(this.nodeType === 3 && /:P/g.test(this.nodeValue)) {
       $(this).parent().html(this.nodeValue.replace(/:P/g,"<img src='http://placehold.it/10x10' />"));
    }
});
Sign up to request clarification or add additional context in comments.

5 Comments

Look into contents() in jQuery... there might be a better way.
@ErikE Well i think this way should work as well, as smileys are not gng to be used as a part of html tag :). But yes using contents and check ing for nodeType would make it more specific...
It just gives me the willies to do a replace on HTML when the content is supposed to be a text node. Saying "not going to be used" is a poor man's bet. +1 now. But wait, how about this example: <img alt="type:Private">. I don't think it's so far-fetched, and would prefer to see you steer people toward the more provably-correct answer!
@ErikE haha thats an awsomely insane example that can come anywhere... yeah you are right. I just got carried away with OPs html snippet... :)
Actually... your second code block has several problems. Try it on <div><div>will this <span title="alt:Private">really</span> work :P</div></div> for some challenges to overcome.
2

I think it is best to not assume that the text will be an immediate child of the passed-in selector. I also see it as a bit chancy to assume that the text you are searching for cannot appear inside a descendant HTML tag. All other answers given so far have issues in at least one of these areas.

Furthermore, it is even better to make the code reusable. The below easily reusable functions will do exactly what you need, won't be messed up by stray HTML tag attributes, and work!

function descendantContents($el, textToFind) { // this could be made a jQuery plugin
   var result = $el
      .find(':not(script)')
      .contents()
      .filter(function () {
         return this.nodeType === 3 && $(this).text().indexOf(textToFind) !== -1;
      });
   return result;
}

function replaceText(scopeSelector, textToFind, replacementHtml) {
   descendantContents($(scopeSelector), textToFind)
      .each(function () {
         var element = $(this);
         var parts = element.text().split(textToFind);
         element.before(document.createTextNode(parts[0]));
         for (var i = 1, l = parts.length; i < l; i += 1) {
            element.before(replacementHtml);
            element.before(document.createTextNode(parts[i]));
         }
         element.remove();
      });
};

These functions have been tested in Firefox 25.0.1, Chrome 30.0.1599.101 m, and 10.0.9200.16721 using jQuery 1.6.1 (I know, that's an old version, but that should make you feel better, not worse).

For anyone wishing to do better, try your code against this HTML:

<div>
   <div>will this <span title="alt:Private">really</span> work :P</div>
</div>

Comments

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.