RSS

cloning a node

29 May

Day 2 of my “active” learning has begun and as item 2 of my TO DO list says I have to write at least one blog post a day – here it is!

Today I was adding some JavaScript functionality to the following form.

form

As you can see on the image at the moment of writing it was possible to enter details only of a single person. So let’s try and let our traveller have a company ๐Ÿ˜‰

The idea is to repeat “Personal Details” section N times. N is a number of people in a group. There are two ways I can think of to make this work – either to clone a fieldset node or to create a each node manually using document.createNode method. The second solution I won’t even try as there are about half of a hundred different HTML elements to create. For this example I chose cloneNode method. Cloning an element has its drawbacks too. Let’s look at examples.

The following code clones a “div1” DIV node with a few child nodes and inserts it in “holder” DIV.

<script type="text/javascript">
var clone = function(){
    var newNode = document.getElementById('div1').cloneNode(true);
     document.getElementById('holder').appendChild(newEntry);
}

</script>
<div id="holder">
    <div id="div1">
    <p>
        <label for="testInput">Input:</label>
        <input name="testInput" id="testInput" value="la la la">
    </p>
    <p>
        <input type="button" value="Fire!">
    </p>
    </div>
</div>

<input type="button" value="Clone it!" onclick="clone();">

Note a parameter I pass into cloneNode. If parameter is set to true then an element will be cloned with all the child nodes it has. Otherwise, if parameter is false, only the node itself is duplicated without its children.

The code above works perfectly well except for one thing – cloneNode doesn’t clone event handlers applied to an element. To make your event handlers work you have to redefine them each time for each cloned node. You will also notice that every child of cloned node and the node itself have the same IDs as the original. To make your HTML code valid you should go through all cloned elements and give them unique IDs. This can be done with setAttribute method. For example,

newNode.firstChild.setAttribute('id', 'newId');

Actually IE does clone events that were set like elem.onclick=... or using attachEvent method. But Firefox and Opera don’t.

There are a few well-known bugs with cloned event handlers in IE. To be on the safe side you’d better make sure you remove all handlers off the element or clone elements before applying any handlers to them.

Advertisements
 
11 Comments

Posted by on May 29, 2009 in HTML, JavaScript

 

Tags: ,

11 responses to “cloning a node

  1. aleahhill

    May 29, 2009 at 12:43 am

    I’ve attempted cloning nodes on a form before using a different method, but IE wouldn’t pass the values of the new input nodes to the next page ๐Ÿ˜ฆ

     
    • Dasha

      May 29, 2009 at 7:02 am

      Did you give these nodes unique names?

       
  2. Dasha

    May 29, 2009 at 9:49 am

    I tried the following code in IE and all values of all fields were passed to the server correctly.

    //Javascript to clone node
    var cloneit = function(){
        var div1 = document.getElementById('div1');
        var newNode = div1.cloneNode(true);
        newNode.getElementsByTagName("input")[0].name = 'newNode';
        newNode.getElementsByTagName("input")[0].value = 'newNodeValue';
        document.forms[0].appendChild(newNode);
    }
    
    //html
    <form method="POST">
    <div id="div1">
        <p><label for="testInput">Input:</label><input name="testInput"  value="la la la"></p>
    </div>
    
    <p><input type="button" value="Fire!" onclick="cloneit();"></p>
    <p><input type="submit" value="Submit form"></p>
    </form>
    

    On form submit I printed form field values out:

    Response.Write(Request.Form("testInput")&"<br>")
    Response.Write(Request.Form("newNode")&"<br>")
    

    And got the following results:

    la la la
    newNodeValue

    Can you provide any details on the problem you get?

     
    • aleahhill

      June 1, 2009 at 9:33 pm

      Got back to work and tried out the script on IE,

      No worky.

      function addOne(){
      //up the guest count
      gnum += 1;
      //make the new names/ids/fors
      var form = 'guestForm'+gnum;
      var first = "Guests[guest"+gnum+"][First]";
      var last = "Guests[guest"+gnum+"][Last]";
      var email = "Guests[guest"+gnum+"][Email]";
      var member = "Guests[guest"+gnum+"][m]";
      /*add a set of fields for a guest*/

      //clone a guest form div
      var newGuest = document.getElementById('guestForm1').cloneNode(true);
      newGuest.setAttribute('id', form);

      //set the new title
      newGuest.getElementsByTagName('h4')[0].firstChild.nodeValue='Guest '+gnum;

      //set the new labels and Id's
      newGuest.getElementsByTagName('label')[0].for = first;
      newGuest.getElementsByTagName('input')[0].id = first;
      newGuest.getElementsByTagName('input')[0].name = first;
      newGuest.getElementsByTagName('input')[0].value = "";
      newGuest.getElementsByTagName('input')[0].readonly = "";
      newGuest.getElementsByTagName('input')[0].class = "changeme";

      newGuest.getElementsByTagName('label')[1].for = last;
      newGuest.getElementsByTagName('input')[1].id = last;
      newGuest.getElementsByTagName('input')[1].name = last;
      newGuest.getElementsByTagName('input')[1].value = "";
      newGuest.getElementsByTagName('input')[1].readonly = "";
      newGuest.getElementsByTagName('input')[1].class = "changeme";

      newGuest.getElementsByTagName('label')[2].for = email;
      newGuest.getElementsByTagName('input')[2].id = email;
      newGuest.getElementsByTagName('input')[2].name = email;
      newGuest.getElementsByTagName('input')[2].value = "";
      newGuest.getElementsByTagName('input')[2].readonly = "";
      newGuest.getElementsByTagName('input')[2].class = "changeme";

      newGuest.getElementsByTagName('label')[3].for = member;
      newGuest.getElementsByTagName('input')[3].id = member;
      newGuest.getElementsByTagName('input')[3].name = member;
      newGuest.getElementsByTagName('input')[3].checked = "";
      newGuest.getElementsByTagName('input')[3].readonly = "";
      newGuest.getElementsByTagName('input')[3].class = "changeme";

      //append the guest form div
      document.getElementById('guestList').appendChild(newGuest);

      //update the fee
      //document.getElementById('total').value = calDue();
      //show take one button
      document.getElementById('take').style.display = 'inline';

      }

      I get Error: Object Expected from IE7, with a wonky line number.
      Works fine in firefox.

       
      • aleahhill

        June 2, 2009 at 5:48 pm

        Turns out IE doesn’t like setting the “for” Attribute that way. (I took out the class lines as well) I ended up removing the “for” node on each label, creating a new attribute node, setting it’s value, then setting it on the label node.

        One of these days I’m going to stop putting off learning JQuery.

        Thanks again for your help. ๐Ÿ˜›

         
  3. aleahhill

    May 30, 2009 at 11:12 am

    The method that I tried before was the “create a each node manually using document.createNode method.” IE wouldn’t let me set an attribute on the radio inputs, i think it was the name attribute (it was a while ago and I’m on a mac now, so no IE to test). Changed everything to text inputs and then it passed nothing for the new nodes, which did have unique names.

     
    • Dasha

      May 30, 2009 at 1:06 pm

      Have a look here – https://dashasalo.wordpress.com/2009/04/26/create-radiobuttons/. I’ve already wrote about this. There is a bug in IE that name and selected properties can’t be set dynamically with setAttribute method.

      The workaround is to use innerHTML. Have a look at the example in that post.

      It should solve the problem!

       
  4. aleahhill

    May 30, 2009 at 12:17 pm

    I tried your way of cloning the node, by using a wrapper element, and OMG it worked great!
    I wish there was a more efficient way of incrementing the names and id’s on the children of the cloned node though. ๐Ÿ™‚

     
    • Dasha

      May 30, 2009 at 1:09 pm

      It is much easier to set all the names and event handlers using a library like JQuery or Prototype. Otherwise it’s like “Mission Impossible” if you have huge number of elements ๐Ÿ™‚

       
  5. aleahhill

    May 30, 2009 at 12:25 pm

    Thank you so much for your help! I had really given up on this script.

     
  6. arroseCek

    June 19, 2009 at 6:37 am

    Thank you for post. It’s very good read.
    I love to read dashasalo.wordpress.com!

    zoom teeth whitening

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: