Reputation: 130085
<div><i>foo</i> and <i>bar</i> go ### with <b>baz</b></div>
I would like to replace the string ###
with another node (<u>well</u>
), without replacing the entire innerHTML
of the wrapper div
.
<div><i>foo</i> and <i>bar</i> go <u>well</u> with <b>baz</b></div>
My approach was to iterate the childNodes, filter only the TEXT_NODE
elements with the string i would like to replace and replace those textNodes with replaceChild
using a DOM Fragment to hold the replaced content:
var root = document.querySelector('div'),
tag = "<u>well</u>",
tempFrag = document.createDocumentFragment(),
children = root.childNodes,
replacedString;
for( var i = children.length; i--; ){
if( children[i].nodeType === Node.TEXT_NODE &&
children[i].nodeValue.length > 1 &&
children[i].nodeValue.indexOf('###') != -1 ){
replacedString = children[i].nodeValue.replace('###', tag);
console.log( replacedString );
tempFrag.innerHTML = replacedString;
children[i].parentNode.replaceChild(tempFrag, children[i])
}
}
<div><i>foo</i> and <i>bar</i> go ### with <b>baz</b></div>
As you can see, replacing a textNode
in this manner doesn't work as expected.
While I can manually extract each part of the replacedString and break it into:
`before textNode` / New element / `after textNode`
and piece them all in, that would create a lot of code (this is actually the way i'm currently doing it, and am trying to think of a smarter way, but the fragment didn't help with the parsing & insertion as you can see)
Upvotes: 2
Views: 176
Reputation: 130085
var root = document.querySelector('div'),
tag = document.createElement('u'),
children = root.childNodes,
replacedNode,
idx;
tag.innerHTML = "well";
for( var i = children.length; i--; ){
if( children[i].nodeType === Node.TEXT_NODE &&
children[i].nodeValue.length > 1 ){
idx = children[i].nodeValue.indexOf('###');
if( idx == -1 ) continue;
replacedNode = children[i].splitText(idx);
// remove '###' from the second split textNode ('### with')
replacedNode.nodeValue = replacedNode.nodeValue.replace('###', '');
// put the tag element before the second split textNode
children[i].parentNode.insertBefore(tag, replacedNode);
}
}
<div><i>foo</i> and <i>bar</i> go ### with <b>baz</b></div>
Upvotes: 0
Reputation: 27460
Instead of this:
replacedString = inputChildren[i].nodeValue.replace('###', tag);
You can use
var offset = ...indexOf('###');
replacementNode = textnode.splitText(offset);
And then by adding
textnode.parent.insertBefore(wrapper, replacementNode);
you can achieve what you want.
Upvotes: 1