23

I want a div to be duplicated when a button is clicked. I though something like this; but it's not working. Can anyone help me?

HTML

<div id="duplicater"> 
duplicate EVERYTHING INSIDE THIS DIV
</div>

JAVASCRIPT

function duplicate()
{
var div = duplicate("div");
    div.id = "duplicater";
div.appendChild(duplicate("duplicater"));
}
peterh
  • 9,698
  • 15
  • 68
  • 87
Opoe
  • 1,307
  • 8
  • 28
  • 55
  • 1
    Do you want to duplicate the div (and end up with two divs), or duplicate what is inside the div? – Jordi Dec 13 '10 at 08:51
  • 2
    I feel like a bit of a broken record saying this, but `id`s *must be unique*. You shouldn't have two elements with `id="duplicater"` on the same page. – nickf Dec 13 '10 at 08:52
  • that makes sense! i'll add a counter – Opoe Dec 13 '10 at 09:08
  • Don't add a counter. Just keep a reference to the created element in JS (possibly storing them in an array). – Quentin Dec 13 '10 at 09:16

3 Answers3

47

You are creating an infinite recursion!

function duplicate()
{
    var div = duplicate("div");

The function is calling itself over and over again. Use cloneNode():

HTML:

<div id="duplicater0"> 
duplicate EVERYTHING INSIDE THIS DIV
</div>

JavaScript:

var i = 0;

function duplicate() {
    var original = document.getElementById('duplicater' + i);
    var clone = original.cloneNode(true); // "deep" clone
   clone.id = "duplicater" + ++i; // there can only be one element with an ID
    clone.onclick = duplicate; // event handlers are not cloned
    original.parentNode.appendChild(clone);
}

Working DEMO

Or without IDs:

function duplicate() {
    var clone = this.cloneNode(true); // "deep" clone
    clone.id = ""; // there can only be one element with an ID
    clone.onclick = duplicate; // event handlers are not cloned
    this.parentNode.appendChild(clone);
}

Update:

If you want to clone the div on button click, you can use a slightly different version:

HTML:

<button id="button" onclick="duplicate()">Click me</button>
<div id="duplicater"> 
    duplicate EVERYTHING INSIDE THIS DIV
</div>

JavaScript:

var i = 0;
var original = document.getElementById('duplicater');

function duplicate() {
    var clone = original.cloneNode(true); // "deep" clone
    clone.id = "duplicater" + ++i;
    // or clone.id = ""; if the divs don't need an ID
    original.parentNode.appendChild(clone);
}

If you are not in a form, you should use <button> instead of <input type="button">.

Working DEMO 2

iBug
  • 30,581
  • 7
  • 64
  • 105
Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
  • Wow thanks a lot, it only works once though! And can i add a button like this? – Opoe Dec 13 '10 at 09:04
  • Thank you so much! You realy helped me out here :) – Opoe Dec 13 '10 at 09:16
  • Ok, this works well, but it doesn't work unless I put the javascript at the end of the . Why is that? I have an external javascript file loaded in the that doesn't work, so I'm going to have to make some changes. – eshellborn Aug 28 '13 at 16:27
  • @eshellborn: It seems you are trying to access an element before it exists! Have a look at this question: http://stackoverflow.com/questions/14028959/why-does-jquery-or-a-dom-method-such-as-getelementbyid-not-find-the-element. – Felix Kling Aug 28 '13 at 16:48
  • Ok thanks. That makes sense to me, but for all other things, my script works in the head, even calling specific elements that "haven't been created yet." So why is it different for this case? – eshellborn Aug 28 '13 at 19:45
  • @eshellborn: I don't think you are doing that, because it's simply impossible. Maybe you put the code in the header but the access is inside some callback. Without knowing the actual code I cannot say more though ;) – Felix Kling Aug 28 '13 at 19:57
  • Is it because I'm calling the functions after the whole page has loaded? Take I look at this: http://goo.gl/m7D4w0, that's kind of what I'm talking about. – eshellborn Aug 28 '13 at 20:29
  • @eshellborn: Yes, exactly. Even though you are putting the code in the header, the DOM access only happens when the function is executed, which is after the DOM was loaded. If you move the `document.getElementById` call before the function, the DOM access would happen when the script tag gets evaluated and would result in an error. – Felix Kling Aug 29 '13 at 04:57
  • Ok thanks for explaining. So as a developer is it good practice to always put the script tag at the end of the ? – eshellborn Aug 29 '13 at 21:37
  • Nice addition to increment the id numbers – JakeCowton Jan 30 '15 at 08:31
  • Hi, your code seems helpful, thanks. However, i'm getting " Cannot read property 'cloneNode' of undefined." error. Do you know how i can handle that(i couldn't find a lot about that on the Internet)? – Dilara Albayrak Oct 07 '16 at 06:59
  • @DilaraAlbayrak: That means that whatever you are calling `cloneNode` on isn't a DOM element. So either the DOM element you are searching for doesn't exist or it doesn't exist *at the moment* you are looking for it, or you are using the DOM API incorrectly. Maybe this helps: http://stackoverflow.com/q/14028959/218196 . – Felix Kling Oct 07 '16 at 12:03
  • upvoted for adding event handlers on the cloned elements. – snow Aug 07 '18 at 11:14
0
<table>
<tr>
<td>
<div id="duplicater"> 
    duplicate EVERYTHING INSIDE THIS DIV
</div>
</td>
</tr>
</table>
<button id="button">Click me</button>

$("#button").on('click', function (e) {
    e.preventDefault();
    var $self = $(this);
    $self.before($self.prev('table').clone());
});
Tanya Sinha
  • 584
  • 5
  • 15
0

Can you change the div to a class? If so, then this might work for you:

# application.js
$(function(){
  $('.photo:last').after('<a href="#" id="new_resource">Add another image<a/><br/>');

  $('a[href=#]').click(function(){
    $(this).before($('.photo:last').clone());

    $('.photo:last input').each(function(){
      $(this).removeAttr("value");
    });

    $('.photo:last input[type=hidden]').remove();

    return false;
  });
})

#html
<div class="photo"
  <label>label</label>  
  <input type="file"></input>
</div>

Edit the code here:

stephenmurdoch
  • 31,696
  • 27
  • 107
  • 175