diff --git a/cjs/dom/parser.js b/cjs/dom/parser.js index ecb31781..544a6ee8 100644 --- a/cjs/dom/parser.js +++ b/cjs/dom/parser.js @@ -34,7 +34,7 @@ class DOMParser { if (isHTML && markupLanguage === '...') markupLanguage = ''; return markupLanguage ? - parseFromString(document, isHTML, markupLanguage) : + parseFromString(document, isHTML, markupLanguage, true) : document; } } diff --git a/cjs/shared/parse-from-string.js b/cjs/shared/parse-from-string.js index 84a9fdf3..61b6244d 100644 --- a/cjs/shared/parse-from-string.js +++ b/cjs/shared/parse-from-string.js @@ -40,7 +40,7 @@ const attribute = (element, end, attribute, value, active) => { const isNotParsing = () => notParsing; exports.isNotParsing = isNotParsing; -const parseFromString = (document, isHTML, markupLanguage) => { +const parseFromString = (document, isHTML, markupLanguage, isDocument = false) => { const {active, registry} = document[CUSTOM_ELEMENTS]; let node = document; @@ -60,6 +60,12 @@ const parseFromString = (document, isHTML, markupLanguage) => { onopentag(name, attributes) { let create = true; if (isHTML) { + // a top level or without gets an implicit , + // so document.head and document.body find the parsed elements + if (isDocument && node === document && (name === 'head' || name === 'body')) { + node = document.firstElementChild || + append(document, document.createElement('html'), false); + } if (ownerSVGElement) { node = append(node, document.createElementNS(SVG_NAMESPACE, name), active); node.ownerSVGElement = ownerSVGElement; diff --git a/esm/dom/parser.js b/esm/dom/parser.js index 1ca423d0..668c15ed 100644 --- a/esm/dom/parser.js +++ b/esm/dom/parser.js @@ -33,7 +33,7 @@ export class DOMParser { if (isHTML && markupLanguage === '...') markupLanguage = ''; return markupLanguage ? - parseFromString(document, isHTML, markupLanguage) : + parseFromString(document, isHTML, markupLanguage, true) : document; } } diff --git a/esm/shared/parse-from-string.js b/esm/shared/parse-from-string.js index 08c6fe39..50b8d422 100644 --- a/esm/shared/parse-from-string.js +++ b/esm/shared/parse-from-string.js @@ -38,7 +38,7 @@ const attribute = (element, end, attribute, value, active) => { export const isNotParsing = () => notParsing; -export const parseFromString = (document, isHTML, markupLanguage) => { +export const parseFromString = (document, isHTML, markupLanguage, isDocument = false) => { const {active, registry} = document[CUSTOM_ELEMENTS]; let node = document; @@ -58,6 +58,12 @@ export const parseFromString = (document, isHTML, markupLanguage) => { onopentag(name, attributes) { let create = true; if (isHTML) { + // a top level or without gets an implicit , + // so document.head and document.body find the parsed elements + if (isDocument && node === document && (name === 'head' || name === 'body')) { + node = document.firstElementChild || + append(document, document.createElement('html'), false); + } if (ownerSVGElement) { node = append(node, document.createElementNS(SVG_NAMESPACE, name), active); node.ownerSVGElement = ownerSVGElement; diff --git a/test/html/document.js b/test/html/document.js index b5e15062..e7607a3e 100644 --- a/test/html/document.js +++ b/test/html/document.js @@ -72,6 +72,12 @@ assert(typeof window.performance.now(), 'number'); assert(parseHTML('
asdf
').document.body.outerHTML, '
asdf
'); +// issue #283: without still backs document.body +const bodyOnly = parseHTML('
').document; +assert(bodyOnly.body, bodyOnly.querySelector('body')); +assert(bodyOnly.documentElement.outerHTML, '
'); +assert(parseHTML('t

').document.documentElement.outerHTML, 't

'); + const location = {href: 'http://ok'}; const withLocation = parseHTML('', {location}); assert(withLocation.document.defaultView.location, location); diff --git a/types/esm/shared/parse-from-string.d.ts b/types/esm/shared/parse-from-string.d.ts index 1cda3495..e20dfcfd 100644 --- a/types/esm/shared/parse-from-string.d.ts +++ b/types/esm/shared/parse-from-string.d.ts @@ -1,2 +1,2 @@ export function isNotParsing(): boolean; -export function parseFromString(document: any, isHTML: any, markupLanguage: any): any; +export function parseFromString(document: any, isHTML: any, markupLanguage: any, isDocument?: boolean): any; diff --git a/worker.js b/worker.js index 31bb78a1..67a6da6f 100644 --- a/worker.js +++ b/worker.js @@ -2269,7 +2269,7 @@ const attribute = (element, end, attribute, value, active) => { attributeChangedCallback$1(element, attribute.name, null, value); }; -const parseFromString = (document, isHTML, markupLanguage) => { +const parseFromString = (document, isHTML, markupLanguage, isDocument = false) => { const {active, registry} = document[CUSTOM_ELEMENTS]; let node = document; @@ -2287,6 +2287,12 @@ const parseFromString = (document, isHTML, markupLanguage) => { onopentag(name, attributes) { let create = true; if (isHTML) { + // a top level or without gets an implicit , + // so document.head and document.body find the parsed elements + if (isDocument && node === document && (name === 'head' || name === 'body')) { + node = document.firstElementChild || + append$2(document, document.createElement('html'), false); + } if (ownerSVGElement) { node = append$2(node, document.createElementNS(SVG_NAMESPACE, name), active); node.ownerSVGElement = ownerSVGElement; @@ -17665,7 +17671,7 @@ class DOMParser { if (isHTML && markupLanguage === '...') markupLanguage = ''; return markupLanguage ? - parseFromString(document, isHTML, markupLanguage) : + parseFromString(document, isHTML, markupLanguage, true) : document; } }