Replace a textNode with mixed-content HTML string









up vote
2
down vote

favorite












For this HTML node:



<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.



Expected result:



<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)










share|improve this question























  • For anyone wondering, it's for my script Tagify
    – vsync
    Nov 10 at 20:12















up vote
2
down vote

favorite












For this HTML node:



<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.



Expected result:



<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)










share|improve this question























  • For anyone wondering, it's for my script Tagify
    – vsync
    Nov 10 at 20:12













up vote
2
down vote

favorite









up vote
2
down vote

favorite











For this HTML node:



<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.



Expected result:



<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)










share|improve this question















For this HTML node:



<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.



Expected result:



<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)






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>





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>






javascript html dom






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 10 at 19:02

























asked Nov 10 at 18:30









vsync

44.4k35154215




44.4k35154215











  • For anyone wondering, it's for my script Tagify
    – vsync
    Nov 10 at 20:12

















  • For anyone wondering, it's for my script Tagify
    – vsync
    Nov 10 at 20:12
















For anyone wondering, it's for my script Tagify
– vsync
Nov 10 at 20:12





For anyone wondering, it's for my script Tagify
– vsync
Nov 10 at 20:12













2 Answers
2






active

oldest

votes

















up vote
1
down vote













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.






share|improve this answer




















  • Thanks. Split helped, I've managed to piece a working example (posted as an answer) but I still had to replace the ### string in replacementNode since it was still present
    – vsync
    Nov 10 at 19:21

















up vote
0
down vote
















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>








share|improve this answer




















    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53242142%2freplace-a-textnode-with-mixed-content-html-string%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote













    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.






    share|improve this answer




















    • Thanks. Split helped, I've managed to piece a working example (posted as an answer) but I still had to replace the ### string in replacementNode since it was still present
      – vsync
      Nov 10 at 19:21














    up vote
    1
    down vote













    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.






    share|improve this answer




















    • Thanks. Split helped, I've managed to piece a working example (posted as an answer) but I still had to replace the ### string in replacementNode since it was still present
      – vsync
      Nov 10 at 19:21












    up vote
    1
    down vote










    up vote
    1
    down vote









    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.






    share|improve this answer












    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.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 10 at 18:40









    c-smile

    20.1k44166




    20.1k44166











    • Thanks. Split helped, I've managed to piece a working example (posted as an answer) but I still had to replace the ### string in replacementNode since it was still present
      – vsync
      Nov 10 at 19:21
















    • Thanks. Split helped, I've managed to piece a working example (posted as an answer) but I still had to replace the ### string in replacementNode since it was still present
      – vsync
      Nov 10 at 19:21















    Thanks. Split helped, I've managed to piece a working example (posted as an answer) but I still had to replace the ### string in replacementNode since it was still present
    – vsync
    Nov 10 at 19:21




    Thanks. Split helped, I've managed to piece a working example (posted as an answer) but I still had to replace the ### string in replacementNode since it was still present
    – vsync
    Nov 10 at 19:21












    up vote
    0
    down vote
















    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>








    share|improve this answer
























      up vote
      0
      down vote
















      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>








      share|improve this answer






















        up vote
        0
        down vote










        up vote
        0
        down vote












        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>








        share|improve this answer















        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>








        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>





        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>






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 10 at 19:20









        vsync

        44.4k35154215




        44.4k35154215



























             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53242142%2freplace-a-textnode-with-mixed-content-html-string%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Top Tejano songwriter Luis Silva dead of heart attack at 64

            ReactJS Fetched API data displays live - need Data displayed static

            政党