javascriptclone

Why doesn't the span get the updated content, or the input get the updated id?


I'm sure it's something simple that I'm missing, but I can't figure it out! The code is definitely being hit (confirmed by console.log statements)

I am expecting that the id for the checkbox gets updated to append _n (where n is the current position) and the span inside the label gets its content changed from ? to SPAN n. As it is, only the label is getting updated as expected and I can't figure out why, or how to make it so that they do get updated. If I update (for example) the span directly it works, as demonstrated near the end of the JS.

Note that this is handwritten quickly as a simplified version of the real thing, so I've skimped on proper validation etc. for this example. I'm using querySelectorAll("*") because in the real code I don't know what the content of the template tag will actually be, so I need to check and update all relevant tags (which is way more than in this example, this is just to highlight the issue).

https://jsfiddle.net/tvqfjokz/11/

document.getElementById("add").addEventListener("click", function() {
  let clone=document.getElementById("template").firstElementChild.cloneNode(true);
  let pos = parseInt(document.getElementById("container").dataset.pos);
  pos++;
  document.getElementById("container").dataset.pos = pos;
  let elements=clone.querySelectorAll("*");
  for (let e of elements) {
    if (e.nodeName==="LABEL") {
      e.innerHTML+=" LABEL "+pos;
    }
    else if (e.nodeName==="INPUT") {
      console.log("Modifying input");
      e.id+="_"+pos;
    }
    else if (e.nodeName==="SPAN") {
      console.log("Modifying span");
      e.innerHTML="SPAN "+pos;
      console.log("Span content is now",e.innerHTML);
    }
  }
  clone.querySelector("span").innerHTML += " This works!";
  console.log("Check id of checkbox (should be checkbox_"+pos+"): ", clone.querySelector("input").id);
  
  document.getElementById("container").appendChild(clone);
});
<div id="template" style="display:none;">
    <div> 
      <label for="checkbox"><input id="checkbox" type="checkbox"> Checkbox <span class="pos">?</span></label>
    </div>
</div>
<button id="add">Add</button>
<div id="container" data-pos="0"></div>

Side note: Originally the template div was in a template tag, I changed it to be a div to make sure the issue wasn't related to the template tag somehow.


Solution

  • as also noted by Pointy, the issue is caused by you setting the entire innerHTML.

    If you want to keep you current logic, you could change your script to something similar to this.

    document.getElementById("add").addEventListener("click", function() {
      let clone=document.getElementById("template").firstElementChild.cloneNode(true);
      let pos = parseInt(document.getElementById("container").dataset.pos);
      pos++;
    
      document.getElementById("container").dataset.pos = pos;
      const elements = clone.querySelectorAll("*");
      for (let e of elements) {
        if (e.nodeName === "LABEL") {
          // note that I am appending a textNode, this way we are in no way altering other elements inside of the label.
          e.appendChild(document.createTextNode(` LABEL ${pos}`))
        }
        else if (e.nodeName === "INPUT") {
          e.id += `_${pos}`;
        }
        else if (e.nodeName === "SPAN") {
          e.textContent = `SPAN ${pos}`;
        }
      }
      document.getElementById("container").appendChild(clone);
    });