4

I am using a recursive function based around for(.. in ..) and hasOwnProperty to clone objects, which works fine in IE and FF... but not Chrome.

When iterating over members of an object using for(... in ...) Firefox and Chrome gives different results for hasOwnProperty if the object is a DOM object.

Typing the following into the Chrome console vs. the console in Firebug(FF) gives different results:

var t = document.createElement("table");
var tr = t.insertRow(-1);
for(var p in tr) if(tr.hasOwnProperty(p)) console.log(p);

Firefox output:

constructor
addEventListener

Chrome output:

clientLeft
scrollHeight
firstElementChild
offsetParent
ch
offsetWidth
isContentEditable
hidden
previousElementSibling
parentElement
localName
children
ownerDocument
nodeValue
lastElementChild
rowIndex
offsetLeft
tagName
className
prefix
innerHTML
previousSibling
namespaceURI
id
childElementCount
innerText
scrollLeft
clientHeight
align
textContent
nextSibling
scrollWidth
offsetHeight
chOff
clientWidth
nodeName
style
lang
scrollTop
offsetTop
childNodes
baseURI
nextElementSibling
vAlign
sectionRowIndex
classList
title
firstChild
attributes
dataset
outerText
cells
parentNode
clientTop
tabIndex
contentEditable
outerHTML
dir
lastChild
bgColor
nodeType
spellcheck
draggable

All the extra properties flagged as true for hasOwnProeperty is causing 'infinite/enough to crash' recusion in my code. Is there a way to determine if a proeperty is a built in DOM object property? Or some other solution..

Mattias
  • 188
  • 6

3 Answers3

4

The easiest solution to this is checking for .cloneNode method and using that if it exists.

This means that your cloning method will check for any DOM nodes and use the pre defined DOM method on it. This should avoid your issue completely.

As for your actual problem. It seems Chrome and Firefox disagree on what belongs on the prototype and what belongs on the object for HTMLTableRowElement (And any other element aswell).

Compare console.dir(HTMLTableRowElement) in both firefox and chrome.

In firefox all those properties live on the HTMLTableRowElement prototype. Where as the chrome prototype only has a few methods on it. (delecteCell and insertCell).

No where in the DOM specification does it say whether propertys of HTMLElements should be defined on the prototype or on the object specifically so this is just something you should not rely on.

Either way use .cloneNode because it's a native method and therefore better/faster then anything you can write in JavaScript.

Chrome psuedo implementation:

function HTMLTableRowElement() {
    ...
    this.nextSibling = ...;
    this.nodeName = ...;
    this.nodeType = ...;
    ...
}

HTMLTableRowElement.prototype.deleteCell = function() { ... };
HTMLTableRowElement.prototype.insertCell = function() { ... };

Firefox pseudo implementation

function HTMLTableRowElement() {
    ...
}

HTMLTableRowElement.prototype.nextSibling = ...;
HTMLTableRowElement.prototype.nodeName = ...;
HTMLTableRowElement.prototype.nodeType = ...;
...
HTMLTableRowElement.prototype.deleteCell = function() { ... };
HTMLTableRowElement.prototype.insertCell = function() { ... };
Raynos
  • 166,823
  • 56
  • 351
  • 396
1

I think @Raynos offers a good solution in his answer. As to why things are so different, I suspect the basic issue is that a DOM element is not a JavaScript object — that is, it does not inherit from the JavaScript "Object" class in any way. DOM elements are provided by the runtime, and have behaviors and semantics that (usually) make sense to JavaScript code, but they're not really JavaScript objects internally. Thus, to me it's somewhat amazing the "hasOwnProperty" is available to be called at all.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • @Pointy I disagree. They are JavaScript objects. They do inherit from Object and they inherit from `HTMLTableRowElement` – Raynos Mar 16 '11 at 12:40
  • @Raynos well I'm not sure that's true. There may be JavaScript object wrappers around them, and I suppose that some JavaScript-in-browser implementations might actually implement them that way, but I see no reason for that to necessarily be the case. Certainly it's true that in other JavaScript environments, "objects" pushed into the global namespace by the runtime are not JavaScript objects, even if they are accessible from JavaScript code. – Pointy Mar 16 '11 at 12:45
  • @Pointy Afaik, all browser objects inherit from JavaScript's `Object.prototype`. It would surprise me if it were any different. For instance, the prototype chain for the `document` object is: `document` --> `HTMLDocument.prototype` --> `Document.prototype` --> `Node.prototype` --> `Object.prototype` --> `null` – Šime Vidas Mar 16 '11 at 12:48
  • @Pointy true. They are not just JavaScript objects. I think there defiantly a superset of JavaScript objects but there still objects just like anything else. For example you cannot call the constructors of their prototypes directly and they do other funny things. – Raynos Mar 16 '11 at 12:48
  • @Raynos @Šime Vidas I offer [this jsfiddle](http://jsfiddle.net/j2zjz/) for you to try out in Internet Explorer, which does not expose a "hasOwnProperty" API on DOM elements. – Pointy Mar 16 '11 at 12:51
  • Again, they *might* be JavaScript objects, but I know of no specification that insists that DOM elements behave exactly as if they are. – Pointy Mar 16 '11 at 12:53
  • @Pointy you are correct. But this is just a case of IE doing it wrong. Try IE9. `(document.createElement("tr") instanceof Object) === false` in IE8. I personally see this as DOM objects/node should be JavaScript objects and that IE is wrong rather then taht they are _not_ javascript objects. – Raynos Mar 16 '11 at 13:09
  • @Raynos well I share your desire that IE should stop being difficult :-) My point is that JavaScript runtimes can make global symbols available (like "window") in many ways, and they don't have to behave like JavaScript objects. It's *nice* if they do, but I don't know of any specification around that (do you?). For example, it's possible to expose Java objects to JavaScript running in Rhino, and they definitely don't behave (much) like JavaScript objects. – Pointy Mar 16 '11 at 13:16
  • @Pointy DOM objects inherit the `hasOwnProperty` property from `Object.prototype` in all current browsers (yes, I am exploiting the fact that IE9 came out yesterday). Prior versions of IE did its own thing, current browsers do it right. Afaik, the ECMAScript standard does not require host objects to inherit from `Object.prototype`, but the browser makers implemented it this way, which is a good thing. However, you are right that DOM objects are not JavaScript objects. – Šime Vidas Mar 16 '11 at 14:24
  • 1
    @SimeVidas I'm loving the phrase "all current browsers" I can now pretend IE8 no longer exists. Dropping IE8 support is going to be wonderful. – Raynos Mar 16 '11 at 16:23
0

The easiest way to determine whether or not an object is a DOM object would be to check if that object has the nodeName, nodeValue or nodeType properties.

if ( tr.nodeType > 0 ) {
    // tr is a DOM object
} else {
    // tr is not a DOM object
}

All DOM objects implement the Node interface and therefore contain the above mentioned properties.

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object – CAFxX Mar 16 '11 at 14:41
  • @CAF I believe my method is good enough... it is the simplest one for sure. The leading answer in that question is overkill imho. – Šime Vidas Mar 16 '11 at 23:03