Is it possible to clone html element objects in JavaScript?
Using your code you can do something like this in plain JavaScript using the cloneNode() method:
// Create a clone of element with id ddl_1:
let clone = document.querySelector('#ddl_1').cloneNode( true );
// Change the id attribute of the newly created element:
clone.setAttribute( 'id', newId );
// Append the newly created element on element p
document.querySelector('p').appendChild( clone );
Or using jQuery clone() method (not the most efficient):
$('#ddl_1').clone().attr('id', newId).appendTo('p'); // append to where you want
How can I clone HTML Nodes with custom properties?
Attributes in HTML are string values, not JavaScript Objects and JavaScript Properties. The cloneNode
operation only clones HTML intrinsics and not anything you add on top, it is not the same thing as a deep object copy.
You will need to do it manually:
function cloneCustomNode(node) {
var clone node.cloneNode(); // the deep=true parameter is not fully supported, so I'm not using it
clone.obj = node.obj; // this will copy the reference to the object, it will not perform a deep-copy clone of the 'obj' object
return clone;
}
This can be generalised to copy any custom JavaScript properties from one object to another, excluding those already defined in the default (defaultNode
).
var defaultNode = document.createElement("div");
function cloneNodeWithAdditionalProperties(node) {
var clone node.cloneNode();
for(var propertyName in node) {
if( !( propertyName in genericNode ) ) {
clone[ propertyName ] = node[ propertyName ];
}
}
return clone;
}
cloneNodeWithAdditionalProperties
will run in O( n )
time because the if( x in y )
operation is a hashtable lookup with O( 1 )
complexity (where n
is the number of properties).
How do I clone an HTML element's style object using JavaScript or jQuery?
var curr_style;
if (window.getComputedStyle) {
curr_style = window.getComputedStyle(el);
} else if (el.currentStyle) {
curr_style = $.extend(true, {}, el.currentStyle);
} else {
throw "shit browser";
}
style
has non-enumerable properties which makes .extend
fall over. You want to use the getComputedStyle
method to get the styles of an element.
You also want to support older versions of IE by extending el.currentStyle
which does have enumerable properties.
The first argument (when set to true
) tells jQuery to do a deep clone.
Removing and cloning a DOM element in Javascript
I tried
let clone = document.getElementById('template').cloneNode();
but that seems to just clone the element without the contents...
Right. To clone its contents, you pass true
into cloneNode
.
let template = document.getElementById('template');
template.remove(); // Or on older browsers: template.parentNode.removeChild(template);
template.removeAttribute("id");
template.removeAttribute("style");
// ...
const clone = template.cloneNode(true);
const input = clone.querySelector(".p-input");
But remember that the original code removes the element, which this doesn't. (It's not clear to me why it removes it and then clones it, but...)
You said in a comment that you didn't really understand why it was being removed and then the id
and such removed from it. It's a fairly common pattern in pages driven by direct DOM manipulation. The markup for the page includes the #template
element, then on DOM load code grabs the #template
element from the page, removes it (so it's not in page anymore), and removes the id
it used to find it on the page. (The style
thing is slightly unusual, but my guess is that it starts out with style="display: none"
so it doesn't show during page load.) Then, as necessary, it clones that element and uses the clone on the page.
Here's a simple example of it at work:
let template = document.getElementById('template');let list = template.parentNode;template.remove(); // Or on older browsers: template.parentNode.removeChild(template);template.removeAttribute("id");template.removeAttribute("style");
document.getElementById("btn-add").addEventListener("click", function() { const textField = document.getElementById("new-text"); const valueField = document.getElementById("new-value"); const text = textField.value; const value = valueField.value; if (text) { const clone = template.cloneNode(true); clone.querySelector(".text").textContent = text; clone.querySelector(".value").value = value; list.appendChild(clone); textField.value = ""; valueField.value = ""; textField.focus(); }});
<div> Add an entry: <div><label>Text: <input type="text" id="new-text"></label></div> <div><label>Value: <input type="text" id="new-value"></label></div> <div><input type="button" id="btn-add" value="Add"></div></div><hr><div> The list: <ul> <li id="template" style="display: none"> <span class="text"></span> <input class="value" type="text"> </li> </ul></div>
How do I correctly clone a JavaScript object?
2022 update
There's a new JS standard called structured cloning. It works in many browsers (see Can I Use).
const clone = structuredClone(object);
Old answer
To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone
method to Object.prototype
, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype
, or other intermediate prototypes, that you don't know about? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty
method.
In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype
is a hidden property of a function. Also, an object's prototype is referenced with the attribute __proto__
, which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes. I think __proto__
might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.
Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object's prototype is Object
, then simply creating a new general object with {}
will work, but if the source's prototype is some descendant of Object
, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty
filter, or which were in the prototype, but weren't enumerable in the first place. One solution might be to call the source object's constructor
property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes. For example, a Date
object stores its data as a hidden member:
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
var d1 = new Date();
/* Executes function after 5 seconds. */
setTimeout(function(){
var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);
The date string for d1
will be 5 seconds behind that of d2
. A way to make one Date
the same as another is by calling the setTime
method, but that is specific to the Date
class. I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!
When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object
, Array
, Date
, String
, Number
, or Boolean
. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object
or Array
would also be one of the 6 simple types in that list. This can be accomplished with code like the following:
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn't more than one reference to the same data in the object. For example:
// This would be cloneable:
var tree = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"right" : null,
"data" : 8
};
// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;
It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.
How to get clone of HTML, modify it, get HTML string without affecting DOM?
You could clone your #wrap
element, modify the as you desire, and append it to a new element, that doesn't exists on the DOM:
var cloned = $('#wrap').clone(),
container = $('<div></div>');
cloned.find('div').andSelf().removeAttr('style');
alert(container.append(cloned).html());
Check the above example here.
How to access an underlying element's ID inside of a javascript clone?
You can just use clone
and set its properties.
let countriesCounter = 1;
document.getElementById("add-line").addEventListener("click", addtlCountry);
function addtlCountry() { let addtlRow = document.getElementById('addtl_country_0'); let table = document.getElementById('input_values'); let clone = addtlRow.cloneNode(true);
clone.id = "addtl_country_" + countriesCounter; const select = clone.querySelector("#countryToVisit"); select.id = "countryToVisit_" + countriesCounter; // or change to "countryToVisit[" + countriesCounter + "]" select.name = "countryToVisit_" + countriesCounter;
table.appendChild(clone); countriesCounter++;}
<table id="input_values"> <tr id="addtl_country_0"> <td> <select name="countryToVisit" id="countryToVisit" required=""> <option selected disabled value="select-country">Select Country</option> <option value="uk">United Kingdom</option> <option value="germany">Germany</option> <option value="chinahk">China/Hong Kong</option> </select> </td> </tr></table><button type="button" id="add-line">Add</button>
Mirroring or clonning a div to another div
Cloning a DOM element can be achived by using the cloneNode
method of the Node
interface.
In your case:
<div id="testDiv">
<!-- ... -->
</div>
<div id="testDivClone"> <!-- Not a canvas element anymore -->
</div>
let source = document.querySelector('#testDiv');
let dest = document.querySelector('#testDivClone');
let clone = source.cloneNode(true); // true -> deep cloning, i.e. the whole subtree
dest.appendChild(clone);
However, be aware that this will not clone any event listeners previously added to the source node or any of its children. Additionally, cloning nodes may lead to duplicate elements with the same ID.
See https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode for more information.
Related Topics
Replace a Regex Capture Group with Uppercase in JavaScript
Prevent Text Selection After Double Click
How to Extend a Class Without Having to Use Super in Es6
Why Does Settimeout() "Break" for Large Millisecond Delay Values
Completely Removing Duplicate Items from an Array
How to Use Jquery in Chrome Extension
Conditional Comment for 'Except IE8'
What's the Difference Between Putting Script in Head and Body
Extract the Text Out of HTML String Using JavaScript
Increment a Number in a String in with Regex
How to Delete Document from Firestore Using Where Clause
How to Inherit from a Class in JavaScript
JavaScript Sort Function. Sort by First Then by Second
Unexpected JavaScript Date Behavior
How to Pass the This Context to a Function