javascriptdom

Replacing `<span>`with text node: Is it a programmer error, or a browser error?


I'm trying to replace some placeholders in my HTML that look like <span id="Something" />. Using the following JavaScript code only replaced the first occurrence, and I wonder why:

function set_placeholder(cls, txt)
{
    txt = document.createTextNode(txt);
    for (var e of document.getElementsByClassName(cls)) {
        e.parentNode.replaceChild(txt, e);
    }
}

It seems the collection is unable to locate the next match after the span has been replaced.

So I tried to insert the text instead, using this variant:

function set_placeholder(cls, txt)
{
    for (var e of document.getElementsByClassName(cls)) {
        e.innerText = txt;
    }
}

Now all occurrences are replaced, making me wonder whether it is my fault, or the browser's (Firefox 102) when the first variant fails.

HTML Sample

The actual HTML is much more complex, but here's some sample:

<html>
 <p><span class="ph-customer" /> bestellte am <span class="ph-customer-date" /> folgende Artikel:</p>
<!-- ... -->
 <table>
  <tr>
    <td><span class="ph-customer-date" />,
    <span class="ph-customer-name" /></td>
  </tr>
 </table>
<!-- ... -->
</html>

So for example the ph-customer-date occurs twice, but calling set_placeholder('ph-customer-date', '30.12.2023') would only replace the first occurrence.


Solution

  • actually in your case it is not an iterator propblem is much more a logical problem let me explain:

    function set_placeholder(cls, txt)
    {
        // ok wrong place and wrong variable name but nevermind
        //(you need to create the text node everytime you need to replace an element)
        txt = document.createTextNode(txt);
        
        for (var e of document.getElementsByClassName(cls)) {
            //here start the paradox( just like a wormhole and alternative realities ^_^ )
            // when you call the replaceChild your next document.getElementsByClassName(cls)
            // will return a different list (an alternative reality) 
            // and your next element (e) will be different from what you expect
            e.parentNode.replaceChild(txt, e);
        }
    }
    

    so for make it short this is the most similar approach to your version:

    function set_placeholder(cls, txt=''){
      var e =document.getElementsByClassName(cls);
      while( e.length>0){
        e[0].parentNode.replaceChild(document.createTextNode(txt),e[0]);
        e=document.getElementsByClassName(cls);
      }
    }
    

    and this is a more compact version of the same approach:

    function set_placeholder(cls, txt=''){
      while( e=document.querySelector("."+cls)){
        e.parentNode.replaceChild(document.createTextNode(txt),e);
      }
    }
    

    plus you could refine the code limiting action to a certain container and not to the whole document in this way:

    function set_placeholder(cls, txt='',elementContainer=document){
      while( e=elementContainer.querySelector("."+cls)){
        e.parentNode.replaceChild(elementContainer.createTextNode(txt),e);
      }
    }