Why does cloneNode exclude custom properties? - javascript

This is related to the question javascript cloneNode and properties.
I'm seeing the same behaviour. Node.cloneNode does not copy over any properties that I add myself (code from original post):
var theSource = document.getElementById("someDiv")
theSource.dictator = "stalin";
var theClone = theSource.cloneNode(true);
alert(theClone.dictator);
theClone does not contain any property "dictator".
I haven't been able to find any explanation for why this is the case. The documentation on MDN states that cloneNode "copies all of its attributes and their values", a line which is taken directly from the DOM specification itself.
This seems broken to me as it makes it next to impossible to do a deep copy of a DOM tree that contains custom properties.
Am I missing something here?

A property is not equal to an attribute.
Use setAttribute() and getAttribute() instead.
var theSource = document.getElementById("someDiv")
theSource.setAttribute('dictator','stalin');
var theClone = theSource.cloneNode(true);
alert(theClone.getAttribute('dictator'));

Not every property corresponds to an attribute. Adding a custom property to an element does not add an attribute, so what happens when you do that is not covered by the DOM spec.
In fact, what happens when you add a property to a host object (such as a DOM node) is completely unspecified and is by no means guaranteed to work, so I'd strongly recommend against doing it. Instead, I'd suggest using wrappers if you want to extend the functionality of host objects (as jQuery and many other libraries do).

Tested this. cloneNode does include the custom attribute in the clone, but that attribute can't be retrieved directly. Try:
var theSource = document.getElementById("someDiv")
theSource.dictator = "stalin";
//or better / more cross browser compatible
theSource.setAttribute('dictator','stalin');
var theClone = theSource.cloneNode(true);
alert(theClone.getAttribute('dictator')); //so, use getAttribute
It may be a browser problem with cloning expando properties. I ran a testcase (see later) from this rather old bugzilla report. It didn't work in Chrome and Firefox (both latest versions).
//code from testcase # bugzilla
var a = document.createElement("div");
a.order = 50;
alert(a.order);
b = a.cloneNode(true);
alert(b.order);

Related

removeChild, textContent and appendChild are deprecated. Alternatives?

I ask this question based on the following link: https://developer.mozilla.org/en-US/docs/Web/API/Attr
I see so many developers use these methods and honestly I am not entirely sure if they are really deprecated as a whole or just in a certain context. Either way, it does list them as deprecated, which is why im asking: what valid and up-to-date alternatives are there? Specifically for:
removeChild, textContent, appendChild. Even insertBefore is deprecated.
My guess is that you are confusing node objects and attribute objects. The functions you named are commonly used with the former, but not the latter. The MDN article is only describing attribute objects.
For more information:
A <div> is a node, and it could have an attribute data-foo="bar" on it.
It is common to append children or modify text to a node. For example, given:
var node = document.createElement('div');
node.innerHTML = '<p>Hello</p><p> World</p>';
node.removeChild(node.firstChild);
would produce HTML of <div><p> World</p></div>
However, this doesn't really make sense for attributes. Attributes are essentially just strings attached to a node. The common actions that are used with attributes are name and value. For example:
var node = document.createElement('div');
node.setAttribute('data-foo', 'bar');
var attr = node.getAttributeNode('data-foo');
console.log(attr.name); // prints 'data-foo'
console.log(attr.value); // prints 'bar'
console.log(typeof attr); // prints 'object'
At least personally, I have rarely had use for the Attr object, instead node.getAttribute('data-foo') returns a string of bar, and that is sufficient for my needs.
As to why those methods exist in the first place on attr objects, I will simply copy/paste from the MDN article you linked:
Warning: In DOM Core 1, 2 and 3, Attr inherited from Node. This is no longer the case in DOM4. In order to bring the implementation of Attr up to specification, work is underway to change it to no longer inherit from Node. You should not be using any Node properties or methods on Attr objects.

DOM4: Deprecated properties and methods, what does it mean?

"Warning: In DOM Core 1, 2 and 3, Attr inherited from Node. This is no longer the case in DOM4. In order to bring the implementation of Attr up to specification, work is underway to change it to no longer inherit from Node . You should not be using any Node properties or methods on Attr objects. Starting in Gecko 7.0 (Firefox 7.0 / Thunderbird 7.0 / SeaMonkey 2.4) , the ones that are going to be removed output warning messages to the console. You should revise your code accordingly. See Deprecated properties and methods for a complete list."
Scrolling down the page, we can see replacements for nodeName and NodeValue, using Attr.name and Attr.value.
https://developer.mozilla.org/en/DOM/Attr#Deprecated_properties_and_methods
What does it really mean for other methods like attributes or childNodes?
The reference says it is deprecated but they don't give any replacement!
It is deprecated for an Attribute but is it for a Node too?
Attr object: http://www.w3schools.com/jsref/dom_obj_attr.asp
Edit: nodeValue will ONLY be deprecated for Attributes (Attr) since Attr will not inherit from a Node anymore in DOM Level 4:
Here's a quick example that helped me to understand:
<div id="myAttribute">myTextNode</div>
var myDiv = document.getElementById("myAttribute");
// If you want to get "myAttribute" from div tag
alert(myDiv.attributes[0].value);
// Correct way to get value of an attribute (displays "myAttribute")
alert(myDiv.attributes[0].nodeValue);
// Working too but deprecated method for Attr since it doesn't inherit from Node in DOM4 (.nodeValue is specific to a Node, not an Attribute)
// If you want to get "myTextNode" from div tag
alert(myDiv.childNodes[0].value);
// Not working since .value is specific to an attribute, not a Node (displays "undefined")
alert(myDiv.childNodes[0].nodeValue);
// Working, .nodeValue is the correct way to get the value of a Node, it will not be deprecated for Nodes! (displays "myTextNode")
Maybe this will avoid confusion to others when accessing Attributes/Nodes :)
What they are saying is that objects that were Attr instances (e.g. such as those returned by Element.getAttributeNode()), used to have properties that it inherited from Node.
However, because this is not the case in DOM4, they are trying to remove this inheritance. Because of this, when you now get an instance of a Attr object, the properties listed in the deprecated list will behave as they're documented.
The big question: It is deprecated for an Attribute but is it for a Node too?: No, they are not deprecated. You can see the list of properties Node has from it's own documentation page.
Attr objects aren't used much (ever?) anyway; are you sure this concerns you?

HTML DOM Extension = bad, but is this OK?

So I realize that in no way do I want to do:
Element.protoype.myfunc = function () {}
But, is this the same or not and is this a good practice?
var e = document.querySelector(q);
e.html = function (html) {
this.innerHTML = html;
}
e.html("Am I in trouble?");
Extending Element will not work in all browsers (notably IE<8). See also this SO question
Extending single elements may result in memory leaks: if such elements are deleted, the method can still exist, containing a link to the non existent element. See this link (it's about handler methods, but it can also apply to extension methods afaik).

How to assign a variable to an element in Javascript like in Actionscript?

In Actionscript I can do
var thing:MovieClip = new MovieClip ();
thing.somevar = 1;
and the thing object would have a variable called somevar.
Can I do the same in Javascript if I created an element using createElement?
Yes, you'd be creating a custom/unsupported attribute for the HTML element that you can read back. Most people will say that it's not good practice but it does work on all browsers*.
Be careful though to avoid circular references. IE cannot free the memory used by objects with circular references if one of the object is an HTML element. This will result in memory leak on IE. Though other browsers can handle it well enough.
* At least all browsers I've ever tried.
Do you mean this?
var a = document.createElement("input");
alert(a.localName); // gets "input"

Sometimes object.setAttribute(attrib,value) isn't equivalent to object.attrib=value in javascript?

It appears that sometimes object.setAttribute(attrib,value) isn't equivalent to object.attrib=value in javascript?
I've got the following code, which works fine:
var lastMonthBn = document.createElement('input');
lastMonthBn.value='<'; // This works fine
lastMonthBn.type='button'; // This works fine
But the following code doesn't:
var div = document.createElement('div');
div.class = 'datepickerdropdown'; // No luck here!
So i need to use the following:
div.setAttribute('class','datepickerdropdown');
My question is, why? From reading this, I thought that object.setAttribute(blah,value) was the same as object.blah=value??
Properties and Attributes aren't really the same, however the DOM exposes standard attributes through properties.
The problem you're facing specifically with the class attribute is that class is a future reserved word.
In some implementations the use of a future reserved word can cause a SyntaxError exception.
For that reason, the HTMLElement DOM interface provides a way to access the class attribute, through the className property:
var div = document.createElement('div');
div.className = 'datepickerdropdown';
Remember, attributes aren't the same as properties, for example:
Immagine a DOM element that looks like this:
<div></div>
If you add a custom attribute to it, e.g.:
myDiv.setAttribute('attr', 'test');
An attribute will be added to the element:
<div attr="test"></div>
Accessing attr as a property on the div element, will simply give you undefined (since is not a property).
myDiv.foo; // undefined
If you bind a property to an element, e.g.:
myDiv.prop = "test";
The getAttribute method will not be able to find it, (since is not an attribute):
myDiv.getAttribute('test'); // null
Note: IE wrongly messes up attributes and properties. :(
As I've said before, the DOM exposes standard attributes as properties, but there are some exceptions that you'll have to know:
The class attribute, is accessible through the className property (the problem you have).
The for attribute of LABEL elements, is accessible through the htmlFor property (collides with the for statement).
Attributes are case-insensitive, but the language bindings for JavaScript properties are not, so the convention is to use the names is camelCase to access attributes through properties, for example the ones formed by two words, e.g. cellSpacing, colSpan, rowSpan, tabIndex, maxLength, readOnly frameBorder, useMap.
It should be noted that browsers like Safari will NOT run JavaScript if keywords like "class" or "int" are present.
So it's a cross-browser support sort of thing. "class" is present in JS2.0 [I believe a package system is available there too]
...
I should also note that in IE, setAttribute [for non-class things, since setAttribute should be use-able for other members such as "style"] can be glitchy.

Categories