Search code examples
javascriptclone

javascript clone a tree of elements and change all the IDs in the clone


i created a button that allows you to clone a tree of elements. This is my code:

   function add_party(number) {
    var n = number.replace("party", ""); 
    var newparty = document.getElementById("party"+n);
    var div = document.createElement("div");
    var con = document.getElementById("party1").innerHTML;
    document.createElement("div");
    div.id = 'party' + ++n;
    div.innerHTML = con;
    newparty.parentNode.insertBefore(div, newparty.nextSibling);
    renumberDivs(div, n, "div","party");
   }

This is my HTML:

   <div id="party1">
    <h1>Party 1</h1>
    <table>
     <tbody>
      <tr>
       <th>party name:</th>
       <td><input type="text" name="name1" value="some name"></td>
     </tr>
     <tr>
      <th>party time:</th>
      <td><input type="text" name="time1" value="some time"></td>
     </tr>
    </tbody>
   </table>
   <input type="button" onclick="add_party(this.parentNode.id)" value="add party">
  </div>

However, since each element has an unique ID or name the clone should not have the same IDs or names. Instead the new IDs and names should add up by 1. So the cloned tree should look like this:

   <div id="party2">
    <h1>Party 2</h1>
    <table>
     <tbody>
      <tr>
       <th>party name:</th>
       <td><input type="text" name="name2" value=""></td>
     </tr>
     <tr>
      <th>party time:</th>
      <td><input type="text" name="time2" value=""></td>
     </tr>
    </tbody>
   </table>
   <input type="button" onclick="add_party(this.parentNode.id)" value="add party">
  </div>

Also, the input values should be empty for the clone. How can i do it with javascript? (no jQuery please).

Thank you.

EDIT:

this code updates the count numbers so they always appear in order:

function renumberDivs(el, n, tagn, ass) {
    while (el = el.nextSibling) {
    if (el.tagName && el.tagName.toLowerCase() == tagn){
      el.id = ass + ++n;
     }
    }
   }

Solution

  • Generally, this solution is a 2-step process:

    1. manipulate existing parties one by one from the last to the insert point.
    2. insert new party to the insert point.

    function add_party(divId) {
        var party = document.getElementById(divId);
        var newParty = party.cloneNode(true);
        var allParties = document.querySelectorAll('div[id^="party"]');
        var newId = parseInt(divId.replace('party', ''), 10) + 1;
    
        for(var i = allParties.length-1; i > newId-2; i--) {
            allParties[i].setAttribute('id', 'party' + (i+2));
            allParties[i].querySelector('h1').innerHTML = 'Party ' + (i+2);
            var partyInputs = allParties[i].querySelectorAll('input[type="text"]');
            for(var j = 0; j < partyInputs.length; j++) {
                var prefix = partyInputs[j].getAttribute("name").replace(/\d+/, "");
                partyInputs[j].setAttribute("name", prefix + (i+2));
            }
        }
    
        newParty.setAttribute('id', 'party' + newId);
        newParty.querySelector('h1').innerHTML = 'Party ' + newId;
        var inputs = newParty.querySelectorAll('input[type="text"]');
        for(var i = 0; i < inputs.length; i++) {
            var prefix = inputs[i].getAttribute("name").replace(/\d+/, "");
            inputs[i].setAttribute("name", prefix + newId);
            inputs[i].setAttribute("value", "");
            inputs[i].value = "";
        }
    
        party.parentNode.insertBefore(newParty, party.nextSibling);
    }
    <div id="party1">
        <h1>Party 1</h1>
        <table>
            <tbody>
                <tr>
                    <th>party name:</th>
                    <td><input type="text" name="name1" value="some name"></td>
                </tr>
                <tr>
                    <th>party time:</th>
                    <td><input type="text" name="time1" value="some time"></td>
                </tr>
            </tbody>
        </table>
        <input type="button" onclick="add_party(this.parentNode.id)" value="add party">
    </div>