Yaron
Yaron

Reputation: 1965

document.querySelector() returns an element that's not an instance of Node

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

Answers (1)

JLRishe
JLRishe

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

Related Questions