Merge pull request #454 from lisa0314/fix/452-document-fragment-insertion
fix(js): flatten DocumentFragment during node insertion
This commit is contained in:
2 files changed
+122
No files matched your search
Vendored
+16
@@ -663,6 +663,11 @@ class Node {
|
|||||||
get previousSibling() { return _wrap(+_dom("prev_sibling", this._nid)); }
|
get previousSibling() { return _wrap(+_dom("prev_sibling", this._nid)); }
|
||||||
appendChild(c) {
|
appendChild(c) {
|
||||||
if (!c) return c;
|
if (!c) return c;
|
||||||
|
if (c instanceof DocumentFragment) {
|
||||||
|
const children = Array.from(c.childNodes);
|
||||||
|
for (const child of children) this.appendChild(child);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
_dom("append_child", this._nid, c._nid);
|
_dom("append_child", this._nid, c._nid);
|
||||||
if (globalThis.__mutationObservers?.length) globalThis.__notifyMutation('childList', this._nid, [c._nid], []);
|
if (globalThis.__mutationObservers?.length) globalThis.__notifyMutation('childList', this._nid, [c._nid], []);
|
||||||
if (c instanceof Element && c.tagName === 'SCRIPT') {
|
if (c instanceof Element && c.tagName === 'SCRIPT') {
|
||||||
@@ -745,6 +750,12 @@ class Node {
|
|||||||
}
|
}
|
||||||
replaceChild(newChild, oldChild) {
|
replaceChild(newChild, oldChild) {
|
||||||
if (!oldChild || !newChild) return oldChild;
|
if (!oldChild || !newChild) return oldChild;
|
||||||
|
if (newChild instanceof DocumentFragment) {
|
||||||
|
const children = Array.from(newChild.childNodes);
|
||||||
|
for (const child of children) this.insertBefore(child, oldChild);
|
||||||
|
this.removeChild(oldChild);
|
||||||
|
return oldChild;
|
||||||
|
}
|
||||||
_dom("insert_before", newChild._nid, oldChild._nid);
|
_dom("insert_before", newChild._nid, oldChild._nid);
|
||||||
_dom("remove_child", oldChild._nid);
|
_dom("remove_child", oldChild._nid);
|
||||||
return oldChild;
|
return oldChild;
|
||||||
@@ -752,6 +763,11 @@ class Node {
|
|||||||
insertBefore(n, ref) {
|
insertBefore(n, ref) {
|
||||||
if (!n) return n;
|
if (!n) return n;
|
||||||
if (!ref) { this.appendChild(n); return n; }
|
if (!ref) { this.appendChild(n); return n; }
|
||||||
|
if (n instanceof DocumentFragment) {
|
||||||
|
const children = Array.from(n.childNodes);
|
||||||
|
for (const child of children) this.insertBefore(child, ref);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
_dom("insert_before", n._nid, ref._nid);
|
_dom("insert_before", n._nid, ref._nid);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1377,6 +1377,112 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append_child_flattens_document_fragment() {
|
||||||
|
let mut rt = setup_runtime(r#"<main id="host"></main>"#);
|
||||||
|
rt.run_page_init();
|
||||||
|
let result = rt
|
||||||
|
.evaluate(
|
||||||
|
r#"
|
||||||
|
const host = document.getElementById('host');
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
const first = document.createElement('article');
|
||||||
|
const second = document.createElement('article');
|
||||||
|
first.id = 'first';
|
||||||
|
second.id = 'second';
|
||||||
|
first.className = second.className = 'quote';
|
||||||
|
fragment.appendChild(first);
|
||||||
|
fragment.appendChild(second);
|
||||||
|
|
||||||
|
const returned = host.appendChild(fragment);
|
||||||
|
return [
|
||||||
|
returned === fragment,
|
||||||
|
Array.from(host.children).map(node => node.id),
|
||||||
|
host.querySelectorAll('.quote').length,
|
||||||
|
fragment.childNodes.length,
|
||||||
|
first.parentNode === host,
|
||||||
|
first.parentElement === host,
|
||||||
|
];
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
serde_json::json!([true, ["first", "second"], 2, 0, true, true])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_before_flattens_document_fragment_in_order() {
|
||||||
|
let mut rt = setup_runtime(r#"<main id="host"><article id="last"></article></main>"#);
|
||||||
|
rt.run_page_init();
|
||||||
|
let result = rt
|
||||||
|
.evaluate(
|
||||||
|
r#"
|
||||||
|
const host = document.getElementById('host');
|
||||||
|
const last = document.getElementById('last');
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
const first = document.createElement('article');
|
||||||
|
const second = document.createElement('article');
|
||||||
|
first.id = 'first';
|
||||||
|
second.id = 'second';
|
||||||
|
fragment.appendChild(first);
|
||||||
|
fragment.appendChild(second);
|
||||||
|
|
||||||
|
const returned = host.insertBefore(fragment, last);
|
||||||
|
return [
|
||||||
|
returned === fragment,
|
||||||
|
Array.from(host.children).map(node => node.id),
|
||||||
|
fragment.childNodes.length,
|
||||||
|
first.parentElement === host,
|
||||||
|
second.parentElement === host,
|
||||||
|
];
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
serde_json::json!([true, ["first", "second", "last"], 0, true, true])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn replace_child_flattens_document_fragment_and_removes_old_child() {
|
||||||
|
let mut rt = setup_runtime(
|
||||||
|
r#"<main id="host"><article id="old"></article><article id="tail"></article></main>"#,
|
||||||
|
);
|
||||||
|
rt.run_page_init();
|
||||||
|
let result = rt
|
||||||
|
.evaluate(
|
||||||
|
r#"
|
||||||
|
const host = document.getElementById('host');
|
||||||
|
const old = document.getElementById('old');
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
const first = document.createElement('article');
|
||||||
|
const second = document.createElement('article');
|
||||||
|
first.id = 'first';
|
||||||
|
second.id = 'second';
|
||||||
|
fragment.appendChild(first);
|
||||||
|
fragment.appendChild(second);
|
||||||
|
|
||||||
|
const returned = host.replaceChild(fragment, old);
|
||||||
|
return [
|
||||||
|
returned === old,
|
||||||
|
Array.from(host.children).map(node => node.id),
|
||||||
|
fragment.childNodes.length,
|
||||||
|
old.parentNode === null,
|
||||||
|
first.parentElement === host,
|
||||||
|
second.parentElement === host,
|
||||||
|
];
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
serde_json::json!([true, ["first", "second", "tail"], 0, true, true, true])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inner_html() {
|
fn test_inner_html() {
|
||||||
let mut rt = setup_runtime(r#"<div id="x"><p>Hello</p></div>"#);
|
let mut rt = setup_runtime(r#"<div id="x"><p>Hello</p></div>"#);
|
||||||
|
|||||||
Reference in new issue
Block a user