diff --git a/cjs/interface/element.js b/cjs/interface/element.js
index 34cb95ba..5d513d6a 100644
--- a/cjs/interface/element.js
+++ b/cjs/interface/element.js
@@ -21,6 +21,7 @@ const {
} = require('../shared/symbols.js');
const {
+ htmlToFragment,
ignoreCase,
knownAdjacent,
localCase,
@@ -382,9 +383,7 @@ class Element extends ParentNode {
}
insertAdjacentHTML(position, html) {
- const template = this.ownerDocument.createElement('template');
- template.innerHTML = html;
- this.insertAdjacentElement(position, template.content);
+ this.insertAdjacentElement(position, htmlToFragment(this.ownerDocument, html));
}
insertAdjacentText(position, text) {
diff --git a/cjs/interface/range.js b/cjs/interface/range.js
index 3c3e3895..7f185120 100644
--- a/cjs/interface/range.js
+++ b/cjs/interface/range.js
@@ -5,7 +5,7 @@ const {END, NEXT, PREV, START} = require('../shared/symbols.js');
const {SVGElement} = require('../svg/element.js');
-const {getEnd, setAdjacent} = require('../shared/utils.js');
+const {getEnd, htmlToFragment, setAdjacent} = require('../shared/utils.js');
const deleteContents = ({[START]: start, [END]: end}, fragment = null) => {
setAdjacent(start[PREV], end[NEXT]);
@@ -102,9 +102,7 @@ class Range {
const { commonAncestorContainer: doc } = this;
const isSVG = 'ownerSVGElement' in doc;
const document = isSVG ? doc.ownerDocument : doc;
- const template = document.createElement('template');
- template.innerHTML = html;
- let {content} = template;
+ let content = htmlToFragment(document, html);
if (isSVG) {
const childNodes = [...content.childNodes];
content = document.createDocumentFragment();
diff --git a/cjs/shared/utils.js b/cjs/shared/utils.js
index 339b3b9a..1dfb969e 100644
--- a/cjs/shared/utils.js
+++ b/cjs/shared/utils.js
@@ -47,3 +47,28 @@ const setAdjacent = (prev, next) => {
next[PREV] = prev;
};
exports.setAdjacent = setAdjacent;
+
+/**
+ * @param {import("../interface/document.js").Document} ownerDocument
+ * @param {string} html
+ * @return {import("../interface/document-fragment.js").DocumentFragment}
+ */
+const htmlToFragment = (ownerDocument, html) => {
+ const fragment = ownerDocument.createDocumentFragment();
+
+ const elem = ownerDocument.createElement('');
+ elem.innerHTML = html;
+ const { firstChild, lastChild } = elem;
+
+ if (firstChild) {
+ knownSegment(fragment, firstChild, lastChild, fragment[END]);
+
+ let child = firstChild;
+ do {
+ child.parentNode = fragment;
+ } while (child !== lastChild && (child = getEnd(child)[NEXT]));
+ }
+
+ return fragment;
+};
+exports.htmlToFragment = htmlToFragment;
\ No newline at end of file
diff --git a/esm/interface/element.js b/esm/interface/element.js
index e7f927e1..c98f5f7d 100644
--- a/esm/interface/element.js
+++ b/esm/interface/element.js
@@ -23,6 +23,7 @@ import {
} from '../shared/symbols.js';
import {
+ htmlToFragment,
ignoreCase,
knownAdjacent,
localCase,
@@ -384,9 +385,7 @@ export class Element extends ParentNode {
}
insertAdjacentHTML(position, html) {
- const template = this.ownerDocument.createElement('template');
- template.innerHTML = html;
- this.insertAdjacentElement(position, template.content);
+ this.insertAdjacentElement(position, htmlToFragment(this.ownerDocument, html));
}
insertAdjacentText(position, text) {
diff --git a/esm/interface/range.js b/esm/interface/range.js
index 45defd5e..1967ef4e 100644
--- a/esm/interface/range.js
+++ b/esm/interface/range.js
@@ -4,7 +4,7 @@ import {END, NEXT, PREV, START} from '../shared/symbols.js';
import {SVGElement} from '../svg/element.js';
-import {getEnd, setAdjacent} from '../shared/utils.js';
+import {getEnd, htmlToFragment, setAdjacent} from '../shared/utils.js';
const deleteContents = ({[START]: start, [END]: end}, fragment = null) => {
setAdjacent(start[PREV], end[NEXT]);
@@ -101,9 +101,7 @@ export class Range {
const { commonAncestorContainer: doc } = this;
const isSVG = 'ownerSVGElement' in doc;
const document = isSVG ? doc.ownerDocument : doc;
- const template = document.createElement('template');
- template.innerHTML = html;
- let {content} = template;
+ let content = htmlToFragment(document, html);
if (isSVG) {
const childNodes = [...content.childNodes];
content = document.createDocumentFragment();
diff --git a/esm/shared/utils.js b/esm/shared/utils.js
index 9074afca..50946b2e 100644
--- a/esm/shared/utils.js
+++ b/esm/shared/utils.js
@@ -38,3 +38,27 @@ export const setAdjacent = (prev, next) => {
if (next)
next[PREV] = prev;
};
+
+/**
+ * @param {import("../interface/document.js").Document} ownerDocument
+ * @param {string} html
+ * @return {import("../interface/document-fragment.js").DocumentFragment}
+ */
+export const htmlToFragment = (ownerDocument, html) => {
+ const fragment = ownerDocument.createDocumentFragment();
+
+ const elem = ownerDocument.createElement('');
+ elem.innerHTML = html;
+ const { firstChild, lastChild } = elem;
+
+ if (firstChild) {
+ knownSegment(fragment, firstChild, lastChild, fragment[END]);
+
+ let child = firstChild;
+ do {
+ child.parentNode = fragment;
+ } while (child !== lastChild && (child = getEnd(child)[NEXT]));
+ }
+
+ return fragment;
+};
\ No newline at end of file
diff --git a/test/html/element.js b/test/html/element.js
index 84198acb..3c3fedd8 100644
--- a/test/html/element.js
+++ b/test/html/element.js
@@ -113,22 +113,6 @@ node.nonce = 'abc';
assert(node.nonce, 'abc', 'yes nonce');
node = document.createElement('div');
-node.innerHTML = '
!
';
-assert(node.innerHTML, '!
', 'innerHTML');
-node.insertAdjacentHTML('beforebegin', 'beforebegin');
-node.insertAdjacentHTML('afterend', 'afterend');
-assert(node.toString(), '', 'no element, no before/after');
-node.firstElementChild.insertAdjacentHTML('beforebegin', 'beforebegin');
-assert(node.toString(), '', 'beforebegin works');
-node.firstElementChild.insertAdjacentHTML('afterbegin', 'afterbegin');
-assert(node.toString(), '', 'afterbegin works');
-node.firstElementChild.insertAdjacentHTML('beforeend', 'beforeend');
-assert(node.toString(), 'beforebegin
afterbegin!beforeend
', 'beforeend works');
-node.firstElementChild.insertAdjacentHTML('afterend', 'afterend');
-assert(node.toString(), 'beforebegin
afterbegin!beforeend
afterend
', 'afterend works');
-
-node.firstElementChild.insertAdjacentText('afterend', '');
-assert(node.toString(), 'beforebegin
afterbegin!beforeend
<OK>afterend
', 'insertAdjacentText works');
node.setAttribute('a', '1');
assert(node.attributes[0].name, 'a')
diff --git a/test/interface/element.js b/test/interface/element.js
index 7844f9b7..bff3f829 100644
--- a/test/interface/element.js
+++ b/test/interface/element.js
@@ -31,6 +31,27 @@ assert(htmlDoc.firstChild.getAttribute('content-desc'), '');
assert(htmlDoc.firstChild.outerHTML, '');
assert(htmlDoc.innerHTML, '');
+const htmlNode = htmlDoc.ownerDocument.createElement('div');
+htmlNode.innerHTML = '!
';
+assert(htmlNode.innerHTML, '!
', 'innerHTML');
+htmlNode.insertAdjacentHTML('beforebegin', 'beforebegin');
+htmlNode.insertAdjacentHTML('afterend', 'afterend');
+assert(htmlNode.toString(), '', 'no element, no before/after');
+htmlNode.firstElementChild.insertAdjacentHTML('beforebegin', 'beforebegin');
+assert(htmlNode.toString(), '', 'beforebegin works');
+htmlNode.firstElementChild.insertAdjacentHTML('afterbegin', 'afterbegin');
+assert(htmlNode.toString(), '', 'afterbegin works');
+htmlNode.firstElementChild.insertAdjacentHTML('beforeend', 'beforeend');
+assert(htmlNode.toString(), 'beforebegin
afterbegin!beforeend
', 'beforeend works');
+htmlNode.firstElementChild.insertAdjacentHTML('afterend', 'afterend');
+assert(htmlNode.toString(), 'beforebegin
afterbegin!beforeend
afterend
', 'afterend works');
+
+htmlNode.firstElementChild.insertAdjacentHTML('beforeend', '12');
+assert(htmlNode.toString(), 'beforebegin
afterbegin!beforeend12
afterend
', 'multiple html works');
+
+htmlNode.firstElementChild.insertAdjacentText('afterend', '');
+assert(htmlNode.toString(), 'beforebegin
afterbegin!beforeend12
<OK>afterend
', 'insertAdjacentText works');
+
const htmlDocWithEmptyAttrFromSet = parser.parseFromString(`
`, 'text/html').documentElement; // attribute is in emptyAttributes set is empty
assert(htmlDocWithEmptyAttrFromSet.firstChild.getAttribute('style'), '');
@@ -48,6 +69,27 @@ assert(xmlDoc.firstChild.getAttribute('content-desc'), '');
assert(xmlDoc.firstChild.outerHTML, '');
assert(xmlDoc.innerHTML, '');
+const xmlNode = xmlDoc.ownerDocument.createElement('div');
+xmlNode.innerHTML = '!
';
+assert(xmlNode.innerHTML, '!
', 'innerHTML');
+xmlNode.insertAdjacentHTML('beforebegin', 'beforebegin');
+xmlNode.insertAdjacentHTML('afterend', 'afterend');
+assert(xmlNode.toString(), '', 'no element, no before/after');
+xmlNode.firstElementChild.insertAdjacentHTML('beforebegin', 'beforebegin');
+assert(xmlNode.toString(), '', 'beforebegin works');
+xmlNode.firstElementChild.insertAdjacentHTML('afterbegin', 'afterbegin');
+assert(xmlNode.toString(), '', 'afterbegin works');
+xmlNode.firstElementChild.insertAdjacentHTML('beforeend', 'beforeend');
+assert(xmlNode.toString(), 'beforebegin
afterbegin!beforeend
', 'beforeend works');
+xmlNode.firstElementChild.insertAdjacentHTML('afterend', 'afterend');
+assert(xmlNode.toString(), 'beforebegin
afterbegin!beforeend
afterend
', 'afterend works');
+
+xmlNode.firstElementChild.insertAdjacentHTML('beforeend', '12');
+assert(xmlNode.toString(), 'beforebegin
afterbegin!beforeend12
afterend
', 'multiple html works');
+
+xmlNode.firstElementChild.insertAdjacentText('afterend', '');
+assert(xmlNode.toString(), 'beforebegin
afterbegin!beforeend12
<OK>afterend
', 'insertAdjacentText works');
+
const xmlDocWithEmptyAttrFromSet = parser.parseFromString(``, 'text/xml').documentElement;// attribute is in emptyAttributes set is empty (even for XML)
assert(xmlDocWithEmptyAttrFromSet.firstChild.getAttribute('style'), '');
assert(xmlDocWithEmptyAttrFromSet.firstChild.outerHTML, '');
diff --git a/test/interface/range.js b/test/interface/range.js
index dd1634ea..cb894cd3 100644
--- a/test/interface/range.js
+++ b/test/interface/range.js
@@ -1,6 +1,6 @@
const assert = require('../assert.js').for('Range');
-const {parseHTML} = global[Symbol.for('linkedom')];
+const {parseHTML, DOMParser} = global[Symbol.for('linkedom')];
const {document} = parseHTML('abc
');
@@ -70,3 +70,11 @@ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
range.selectNodeContents(svg);
const rect = range.createContextualFragment('').childNodes[0];
assert('ownerSVGElement' in rect, true, 'createContextualFragment(SVG)');
+
+{
+ const svgDocument = (new DOMParser).parseFromString('', 'image/svg+xml');
+
+ let range = svgDocument.createRange();
+ let contextual = range.createContextualFragment('hi
');
+ assert(contextual.toString(), '<#document-fragment>hi
#document-fragment>', 'createContextualFragment');
+}
diff --git a/types/esm/interface/range.d.ts b/types/esm/interface/range.d.ts
index fd97cb3f..ca51a006 100644
--- a/types/esm/interface/range.d.ts
+++ b/types/esm/interface/range.d.ts
@@ -14,7 +14,7 @@ export class Range implements globalThis.Range {
cloneContents(): any;
deleteContents(): void;
extractContents(): any;
- createContextualFragment(html: any): any;
+ createContextualFragment(html: any): import("./document-fragment.js").DocumentFragment;
cloneRange(): Range;
[START]: any;
[END]: any;
diff --git a/types/esm/shared/utils.d.ts b/types/esm/shared/utils.d.ts
index 46afbdbb..827f1de8 100644
--- a/types/esm/shared/utils.d.ts
+++ b/types/esm/shared/utils.d.ts
@@ -12,4 +12,5 @@ export function localCase({ localName, ownerDocument }: {
ownerDocument: any;
}): any;
export function setAdjacent(prev: any, next: any): void;
+export function htmlToFragment(ownerDocument: import("../interface/document.js").Document, html: string): import("../interface/document-fragment.js").DocumentFragment;
declare const $String: StringConstructor;
diff --git a/worker.js b/worker.js
index 858ae068..d7720bdd 100644
--- a/worker.js
+++ b/worker.js
@@ -3893,6 +3893,30 @@ const setAdjacent = (prev, next) => {
next[PREV] = prev;
};
+/**
+ * @param {import("../interface/document.js").Document} ownerDocument
+ * @param {string} html
+ * @return {import("../interface/document-fragment.js").DocumentFragment}
+ */
+const htmlToFragment = (ownerDocument, html) => {
+ const fragment = ownerDocument.createDocumentFragment();
+
+ const elem = ownerDocument.createElement('');
+ elem.innerHTML = html;
+ const { firstChild, lastChild } = elem;
+
+ if (firstChild) {
+ knownSegment(fragment, firstChild, lastChild, fragment[END]);
+
+ let child = firstChild;
+ do {
+ child.parentNode = fragment;
+ } while (child !== lastChild && (child = getEnd(child)[NEXT]));
+ }
+
+ return fragment;
+};
+
const shadowRoots = new WeakMap;
let reactive = false;
@@ -7969,9 +7993,7 @@ let Element$1 = class Element extends ParentNode {
}
insertAdjacentHTML(position, html) {
- const template = this.ownerDocument.createElement('template');
- template.innerHTML = html;
- this.insertAdjacentElement(position, template.content);
+ this.insertAdjacentElement(position, htmlToFragment(this.ownerDocument, html));
}
insertAdjacentText(position, text) {
@@ -12147,9 +12169,7 @@ class Range {
const { commonAncestorContainer: doc } = this;
const isSVG = 'ownerSVGElement' in doc;
const document = isSVG ? doc.ownerDocument : doc;
- const template = document.createElement('template');
- template.innerHTML = html;
- let {content} = template;
+ let content = htmlToFragment(document, html);
if (isSVG) {
const childNodes = [...content.childNodes];
content = document.createDocumentFragment();