"jQuery way" to replace just a text node with a mix of HTML and text?
Asked Answered
H

3

8

In my web browser userscript project I need to replace just one text node without affecting the other HTML elements under the same parent node as the text. And I need to replace it with more than one node:

<div id="target"> foo / bar; baz<img src="/image.png"></div>

Needs to become:

<div id="target"> <a href="#">foo</a> / <a href="#">bar</a>; <a href="#">baz</a><img src="/image.png"></div>

I know jQuery doesn't have a whole lot of support for text nodes. I know I could use direct DOM calls instead of jQuery. And I know I could just do something like $('#target').html(my new stuff + stuff I don't want to change). Also note that I'd like to preserve the initial space, this on its own seems to be tricky.

What I'd like to ask the experts here is, Is there a most idiomatic jQuery way to do this?

Highgrade answered 12/10, 2012 at 3:17 Comment(5)
How would you know where to break up the replace text?Sismondi
api.jquery.com/contentsHeathheathberry
@j_mcnally: Just assume we have "some way" to generate the replacement text/HTML. In my actual project I'm parsing the original text into some objects which I then use to generate basically the same text but with certain words changed to links.Highgrade
I don't know of any jQuery solution; but I did write a highlighting feature that does something close to what you want to accomplish: jsfiddle.net/rkw79/5cCucPinelli
@rkw: Yes that's definitely a related problem.Highgrade
W
10

You basically want to replace the first child (text node) with the new content. You need http://api.jquery.com/replaceWith/

// Text node we want to process and remove
var textNode = $("#target").contents().first();
// Break the text node up
var parts = textNode.text().split(/\s/);
// Put links around them
var replaceWith = "";
for (var i =0; i < parts.length;i++) {
    replaceWith += "<a href='http://www.google.com'>"  + escapeHTML(parts[i]) + "</a> ";
}
// Replace the text node with the HTML we created
textNode.replaceWith(replaceWith);

function escapeHTML(string) {
  return string.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="target">example, handles escaping properly, you should see an ampersand followed by "amp;" here: &amp;amp; <img src="/foobar.png"></div>

http://jsfiddle.net/NGYTB/1/

Wellington answered 12/10, 2012 at 4:1 Comment(3)
Well the new HTML isn't purely based whitespace vs non whitespace but this approach is definitely very good!Highgrade
@Highgrade How am I supposed to know how you're going to break it up? It's just a simple and clear example so you can modify it to your needs. The relevant part is having access to the text node, and replacing with the HTML you are going to come up withWellington
Sorry that wasn't supposed to be a complaint, just a comment to anyone else reading. I'll be accepting your answer unless something really surprising comes along so don't worry (-:Highgrade
B
1

Try this approach

// nodeType = 1 corresponds to element node
   // You are filtering everything out other than that and 
   // removing the textnodes from it..
   // contents() will get everything including the text
    ​$('#target'​).contents().filter(function() {
        return this.nodeType != 1; 
    }).remove();

   // Then you are prepending the div with the html  that needs to 
   //  replaced with the text...
    $('#target').prepend('<a href="#">mixed</a> text <a href="#">and</a> HTML');
   // This makes sure you do not touch the initial img element inside the div

You can add other node types if you do not want to remove specific nodetypes CHECK FIDDLE​

Boracite answered 12/10, 2012 at 3:40 Comment(1)
This works. I suppose it's not a very general approach but it does seem to be a problem domain slightly outside jQuery's scope and this is idiomatic jQuery.Highgrade
R
0

try this..edited version

$('#target').html('<a href="#">mixed</a> text <a href="#">and</a> HTML' + $('#target').html());
Rigsby answered 12/10, 2012 at 3:26 Comment(3)
But that would result in some text<img src="/image.png"><a href="#">mixed</a> text <a href="#">and</a> HTMLHighgrade
No that doesn't work. the call to .html() returns the old HTML including the old text nodes. So basically it ends up prepending the new stuff rather than replacing the old stuff with the new stuff.Highgrade
Well I don't have code yet. I have the HTML from the website I'm writing the userscript for and I'm just trying out various approaches directly in the JavaScript console. If you really need to see some HTML I'm trying to transform from the site we can do that in an SE chatroom. I have updated my example HTML to look more like my problem though if that helps.Highgrade

© 2022 - 2024 — McMap. All rights reserved.