Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit b3af469

Browse files
committed
Improve accuracy of document stubbing
1 parent edce810 commit b3af469

File tree

1 file changed

+43
-8
lines changed

1 file changed

+43
-8
lines changed

src/index.js

+43-8
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ exports.renderFragment = function render(input) {
8888
function renderNode(rootNode) {
8989
let createdPromises = [];
9090

91-
stubMissingDocumentMethods(rootNode);
91+
var document = getDocument(rootNode);
9292

9393
recurseTree(rootNode, (foundNode) => {
9494
if (foundNode.tagName) {
@@ -99,7 +99,7 @@ function renderNode(rootNode) {
9999
Object.setPrototypeOf(foundNode, customElement);
100100
if (customElement.createdCallback) {
101101
createdPromises.push(new Promise((resolve) => {
102-
resolve(customElement.createdCallback.call(foundNode, rootNode));
102+
resolve(customElement.createdCallback.call(foundNode, document));
103103
}));
104104
}
105105
}
@@ -111,11 +111,46 @@ function renderNode(rootNode) {
111111

112112
/**
113113
* If rootNode is not a real document (e.g. while rendering a fragment), then some methods such as
114-
* createElement are not available. In this case, we proxy these through to the real page document,
115-
* to pretend that you're always rendering your content within a full document.
114+
* createElement are not available. This method ensures you have a document equivalent object: if
115+
* you call normal document methods on it (createElement, querySelector, etc) you'll get what you
116+
* expect.
117+
*
118+
* That means methods independent of page hierarchy, especially those that are only present on
119+
* the true document object (createElement), should be called on the real document, and methods that
120+
* care about document hierarchy (querySelectorAll, getElementById) should be scope to the given node.
116121
*/
117-
function stubMissingDocumentMethods(rootNode) {
118-
var document = rootNode.ownerDocument;
119-
120-
if (!rootNode.createElement) rootNode.createElement = document.createElement.bind(document);
122+
function getDocument(rootNode) {
123+
// Only real documents have a null ownerDocument
124+
if (rootNode.ownerDocument === null) return rootNode;
125+
126+
else {
127+
let document = rootNode.ownerDocument;
128+
129+
var documentMethods = [
130+
'compatMode',
131+
'createTextNode',
132+
'createComment',
133+
'createDocumentFragment',
134+
'createProcessingInstruction',
135+
'createElement',
136+
'createElementNS',
137+
'createEvent',
138+
'createTreeWalker',
139+
'createNodeIterator',
140+
'location',
141+
'title',
142+
'onabort',
143+
'onreadystatechange',
144+
'onerror',
145+
'onload',
146+
];
147+
148+
documentMethods.forEach((propertyName) => {
149+
var property = document[propertyName];
150+
if (typeof(property) === 'function') property = property.bind(document);
151+
rootNode[propertyName] = property;
152+
});
153+
154+
return rootNode;
155+
}
121156
}

0 commit comments

Comments
 (0)