Node.prototype.getRootNode Polyfill
This package provides a polyfill for the `Node.prototype.getRootNode` DOM method, as defined by the WHATWG DOM specification. It allows developers to use this modern DOM API in environments that do not natively support it, ensuring consistent behavior across various browsers and Node.js environments where DOM-like structures might exist (e.g., JSDOM). The current stable version is 1.0.0. Polyfills like this typically have a low release cadence, updating primarily for specification changes or critical bug fixes, as their goal is to align with native browser implementations. Its key differentiator is its focused and spec-compliant implementation of a specific web standard, enabling forward compatibility for applications targeting modern DOM APIs while supporting older runtimes. This project helps bridge the gap for applications needing to support a wide range of browser versions.
Common errors
-
TypeError: someElement.getRootNode is not a function
cause The `Node.prototype.getRootNode` method was called on an element before the polyfill (via `get-root-node-polyfill/implement`) was applied, or in an environment where `Node` is not globally defined and the standalone function was not used.fixAdd `require('get-root-node-polyfill/implement');` at the entry point of your application to ensure the polyfill is active. If in a Node.js environment without a DOM, use a library like JSDOM or explicitly call the standalone function: `const getRootNode = require('get-root-node-polyfill'); getRootNode.call(someElement, options);`. -
ReferenceError: Node is not defined
cause This error occurs in Node.js environments when the polyfill or its related logic expects the global `Node` constructor (part of the DOM API) to be present, but it's not. Standard Node.js does not have a DOM.fixIf working with DOM-like structures in Node.js, integrate a DOM emulation library like `jsdom`. Alternatively, when using `get-root-node-polyfill` directly, ensure you are using the standalone function (`require('get-root-node-polyfill')`) and applying it to your custom objects, rather than relying on `Node.prototype` modification.
Warnings
- gotcha Including `get-root-node-polyfill/implement` multiple times or alongside other `getRootNode` polyfills can lead to conflicts or unexpected behavior if they are not idempotent or implement the spec differently. It may overwrite existing implementations.
- gotcha The `composed` option (defaulting to `false`) dictates whether the `getRootNode` method should traverse beyond shadow boundaries. Misunderstanding this option can lead to incorrect root node identification, especially in applications heavily utilizing Shadow DOM.
- gotcha While this polyfill provides spec-compliant behavior, relying on any polyfill can introduce a slight performance overhead compared to native browser implementations. This is generally negligible but can be a factor in highly performance-sensitive applications or on extremely old/resource-constrained devices.
Install
-
npm install get-root-node-polyfill -
yarn add get-root-node-polyfill -
pnpm add get-root-node-polyfill
Imports
- Node.prototype.getRootNode (polyfill implementation)
require('get-root-node-polyfill/implement'); - getRootNode (standalone function)
import getRootNode from 'get-root-node-polyfill';
const getRootNode = require('get-root-node-polyfill'); - isImplemented
const isImplemented = require('get-root-node-polyfill/is-implemented');
Quickstart
// Quickstart:
// Ensure the polyfill is applied early in your application lifecycle.
// In a browser environment, this would extend the native Node.prototype.
require('get-root-node-polyfill/implement');
// A minimal mock-up of a DOM Node for demonstration in a non-browser environment
// In a real browser application, you'd use actual DOM elements.
class MockNode {
constructor(name, parent = null, host = null) {
this.nodeName = name; // For identification in logs
this.parentNode = parent; // Standard DOM parent
this.host = host; // For elements inside a Shadow DOM, this points to the Shadow Host
}
// Simulate calling the getRootNode method (which is now polyfilled on Node.prototype)
// If MockNode were a real Node, we'd just call this.getRootNode(options)
// For this example, we directly call the polyfilled method, passing 'this' as context.
getTheRootNode(options) {
// The polyfill, once 'implement' is called, makes this available on Node.prototype.
// We simulate its call here for our mock object.
const getRootNodePolyfill = require('get-root-node-polyfill');
return getRootNodePolyfill.call(this, options);
}
}
// 1. Regular DOM-like structure
const docRoot = new MockNode('document');
const body = new MockNode('body', docRoot);
const div = new MockNode('div', body);
const span = new MockNode('span', div);
console.log('--- Regular DOM-like structure ---');
// Get root node for a regular element (span)
let root = span.getTheRootNode({ composed: true });
console.log(`Span root (composed: true): ${root.nodeName} (Expected: document - ${root.nodeName === docRoot.nodeName})`);
root = span.getTheRootNode({ composed: false });
console.log(`Span root (composed: false): ${root.nodeName} (Expected: document - ${root.nodeName === docRoot.nodeName})`);
// 2. Structure involving a "Shadow DOM" host
// The shadow host is 'shadow-host-element'
const shadowHostElement = new MockNode('shadow-host-element', body);
// The shadow root itself is conceptually contained by shadowHostElement.
// For the purpose of getRootNode, an element *inside* a shadow tree has its 'host' property set.
const elementInShadowTree = new MockNode('element-in-shadow-tree', null, shadowHostElement); // parentNode is null, host is shadowHostElement
console.log('
--- Shadow DOM-like structure ---');
// Get root node for element within shadow tree
root = elementInShadowTree.getTheRootNode({ composed: true });
console.log(`Element in Shadow Tree root (composed: true): ${root.nodeName} (Expected: document - ${root.nodeName === docRoot.nodeName})`);
root = elementInShadowTree.getTheRootNode({ composed: false });
console.log(`Element in Shadow Tree root (composed: false): ${root.nodeName} (Expected: shadow-host-element - ${root.nodeName === shadowHostElement.nodeName})`);
// Check if the polyfill was actually applied
const isImplementedNatively = require('get-root-node-polyfill/is-implemented')();
console.log(`
Is Node.prototype.getRootNode natively implemented? ${isImplementedNatively ? 'Yes' : 'No (polyfill active)'}`);