Reputation: 1965
I'm traversing the DOM and one of the divs I analyze looks different than the rest. All other divs:
document.querySelector('#somediv') instanceof Node
// true
But one div:
document.querySelector('#strange_div') instanceof Node
// false
When I check the constructor of that div:
document.querySelector('#strange_div').constructor.name
// HTMLDivElement
And yet, when I test instanceof again:
document.querySelector('#strange_div') instanceof HTMLDivElement
// false
How is it possible ? A div that isn't an instance of Node, yet is an instance of HTMLDivElement
Note The div is of type ELEMENT_NODE
document.querySelector('#strange_div').nodeType
// 1
Checking the __proto__
chain
(7) [div#strange, HTMLDivElement, HTMLElement, Element, Node, EventTarget, {…}]
0: div#strange
1: HTMLDivElement {Symbol(Symbol.toStringTag): "HTMLDivElement", constructor: ƒ}
2: HTMLElement {…}
3: Element {…}
4: Node {…}
5: EventTarget {Symbol(Symbol.toStringTag): "EventTarget", addEventListener: ƒ, dispatchEvent: ƒ, removeEventListener: ƒ, constructor: ƒ}
6: {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
Comparing the Node in the proto chain:
document.querySelector('#strange').__proto__.__proto__.__proto__.__proto__ === Node
// false
Note 2
My intention here is to override the Node element with a specific getter function. This getter is available to all the divs but the one which isn't an instance of Node.
In more details:
childNodes
is extended by some 3rd party library and that's why it's unusable to me.
I created a new iframe and I extend the Node element with a pure reference to the childNodes
getter
const ref =
Object.getOwnPropertyDescriptor(
safeReferenceFromIframe.Node.prototype,
functionName
) || {};
Object.defineProperty(Node.prototype, `pure_${functionName}`, ref);
Upvotes: 3
Views: 2537
Reputation: 101652
How is this possible?
There's no way for me to know exactly why it's happening in your case, but one well-known pitfall of instanceof
is that it does not work across frames.
The Node
in one frame is different from the Node
in another frame. Your page's DOM can contain an element that was created in another frame, and if you use instanceof
on it, it will not inherit from the Node
in your page.
Like this:
const ifr = document.querySelector('iframe');
// Create an <a> element using an iframe's content document
const createdA = ifr.contentDocument.createElement('a');
createdA.textContent = "I am a link";
createdA.href = "http://www.yahoo.com";
// append it to the dom
document.body.appendChild(createdA);
// re-retrieve it
const myA = document.querySelector('a');
console.log('myA instanceof Node', myA instanceof Node); // false
console.log(myA.nodeName); // A
It sounds like you are working within a very chaotic environment - .childNodes
accessors being overridden, mystery elements that don't inherit from Node
.
My primary suggestion would be to find a way to remedy that.
If that's not an option, my next suggestion would be to abandon your attempts of modifying the Node
prototype, and instead create a function that provides the functionality of the built-in childNodes
property.
Looks like your current approach could be adapted in order to do that:
// Converts a prototype method to a function that takes an instance
// of that type as the first argument, followed by any additional arguments
const unbind = (f) => (that, ...args) => f.apply(that, args);
const childNodesDesc =
Object.getOwnPropertyDescriptor(
ifr.contentWindow.Node.prototype,
'childNodes'
);
const childNodes = unbind(childNodesDesc.get);
// use childNodes
console.log(childNodes(strangeDiv));
Upvotes: 3