| <!DOCTYPE html> |
| <meta charset=utf-8> |
| <title>ParentNode.replaceChildren</title> |
| <link rel=help href="https://dom.spec.whatwg.org/#dom-parentnode-replacechildren"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="pre-insertion-validation-hierarchy.js"></script> |
| <script> |
| preInsertionValidateHierarchy("replaceChildren"); |
| |
| function test_replacechildren(node, nodeName) { |
| test(() => { |
| const parent = node.cloneNode(); |
| parent.replaceChildren(); |
| assert_array_equals(parent.childNodes, []); |
| }, `${nodeName}.replaceChildren() without any argument, on a parent having no child.`); |
| |
| test(() => { |
| const parent = node.cloneNode(); |
| parent.replaceChildren(null); |
| assert_equals(parent.childNodes[0].textContent, 'null'); |
| }, `${nodeName}.replaceChildren() with null as an argument, on a parent having no child.`); |
| |
| test(() => { |
| const parent = node.cloneNode(); |
| parent.replaceChildren(undefined); |
| assert_equals(parent.childNodes[0].textContent, 'undefined'); |
| }, `${nodeName}.replaceChildren() with undefined as an argument, on a parent having no child.`); |
| |
| test(() => { |
| const parent = node.cloneNode(); |
| parent.replaceChildren('text'); |
| assert_equals(parent.childNodes[0].textContent, 'text'); |
| }, `${nodeName}.replaceChildren() with only text as an argument, on a parent having no child.`); |
| |
| test(() => { |
| const parent = node.cloneNode(); |
| const x = document.createElement('x'); |
| parent.replaceChildren(x); |
| assert_array_equals(parent.childNodes, [x]); |
| }, `${nodeName}.replaceChildren() with only one element as an argument, on a parent having no child.`); |
| |
| test(() => { |
| const parent = node.cloneNode(); |
| const child = document.createElement('test'); |
| parent.appendChild(child); |
| parent.replaceChildren(); |
| assert_array_equals(parent.childNodes, []); |
| }, `${nodeName}.replaceChildren() without any argument, on a parent having a child.`); |
| |
| test(() => { |
| const parent = node.cloneNode(); |
| const child = document.createElement('test'); |
| parent.appendChild(child); |
| parent.replaceChildren(null); |
| assert_equals(parent.childNodes.length, 1); |
| assert_equals(parent.childNodes[0].textContent, 'null'); |
| }, `${nodeName}.replaceChildren() with null as an argument, on a parent having a child.`); |
| |
| test(() => { |
| const parent = node.cloneNode(); |
| const x = document.createElement('x'); |
| const child = document.createElement('test'); |
| parent.appendChild(child); |
| parent.replaceChildren(x, 'text'); |
| assert_equals(parent.childNodes.length, 2); |
| assert_equals(parent.childNodes[0], x); |
| assert_equals(parent.childNodes[1].textContent, 'text'); |
| }, `${nodeName}.replaceChildren() with one element and text as argument, on a parent having a child.`); |
| |
| async_test(t => { |
| let phase = 0; |
| |
| const previousParent = node.cloneNode(); |
| const insertions = [ |
| document.createElement("test1"), |
| document.createElement("test2") |
| ]; |
| previousParent.append(...insertions); |
| |
| const parent = node.cloneNode(); |
| const children = [ |
| document.createElement("test3"), |
| document.createElement("test4") |
| ]; |
| parent.append(...children); |
| |
| const previousObserver = new MutationObserver(mutations => { |
| t.step(() => { |
| assert_equals(phase, 0); |
| assert_equals(mutations.length, 2); |
| for (const [i, mutation] of Object.entries(mutations)) { |
| assert_equals(mutation.type, "childList"); |
| assert_equals(mutation.addedNodes.length, 0); |
| assert_equals(mutation.removedNodes.length, 1); |
| assert_equals(mutation.removedNodes[0], insertions[i]); |
| } |
| phase = 1; |
| }); |
| }); |
| previousObserver.observe(previousParent, { childList: true }); |
| |
| const observer = new MutationObserver(mutations => { |
| t.step(() => { |
| assert_equals(phase, 1, "phase"); |
| assert_equals(mutations.length, 1, "mutations.length"); |
| const mutation = mutations[0]; |
| assert_equals(mutation.type, "childList", "mutation.type"); |
| assert_equals(mutation.addedNodes.length, 2, "added nodes length"); |
| assert_array_equals([...mutation.addedNodes], insertions, "added nodes"); |
| assert_equals(mutation.removedNodes.length, 2, "removed nodes length"); |
| assert_array_equals([...mutation.removedNodes], children, "removed nodes"); |
| }); |
| t.done(); |
| }); |
| observer.observe(parent, { childList: true }); |
| |
| parent.replaceChildren(...previousParent.children); |
| }, `${nodeName}.replaceChildren() should move nodes in the right order`); |
| } |
| |
| test_replacechildren(document.createElement('div'), 'Element'); |
| test_replacechildren(document.createDocumentFragment(), 'DocumentFragment'); |
| |
| async_test(t => { |
| let root = document.createElement("div"); |
| root.innerHTML = "<div id='a'>text<div id='b'>text2</div></div>"; |
| const a = root.firstChild; |
| const b = a.lastChild; |
| const txt = b.previousSibling; |
| const txt2 = b.firstChild; |
| |
| const observer = new MutationObserver((mutations) => { |
| |
| assert_equals(mutations.length, 2, "mutations.length"); |
| |
| assert_equals(mutations[0].target.id, "a", "Target of the removal"); |
| assert_equals(mutations[0].addedNodes.length, 0, "Should not have added nodes"); |
| assert_equals(mutations[0].removedNodes.length, 1, "Should have 1 removed node"); |
| assert_equals(mutations[0].removedNodes[0], txt, "Should have removed txt node"); |
| |
| assert_equals(mutations[1].target.id, "b", "Target of the replaceChildren"); |
| assert_equals(mutations[1].removedNodes.length, 1, "Should have removed 1 node"); |
| assert_equals(mutations[1].removedNodes[0], txt2, "Should have removed txt2 node"); |
| assert_equals(mutations[1].addedNodes.length, 1, "Should have added a node"); |
| assert_equals(mutations[1].addedNodes[0], txt, "Should have added txt node"); |
| |
| observer.disconnect(); |
| t.done(); |
| }); |
| |
| observer.observe(a, { |
| subtree: true, |
| childList: true |
| }); |
| |
| b.replaceChildren(txt); |
| }, "There should be a MutationRecord for the node removed from another parent node."); |
| |
| async_test(t => { |
| // This is almost the same test as above, but passes two nodes to replaceChildren. |
| |
| let root = document.createElement("div"); |
| root.innerHTML = "<div id='a'><div id='c'></div>text<div id='b'>text2</div></div>"; |
| const a = root.firstChild; |
| const b = a.lastChild; |
| const c = a.firstChild; |
| const txt = b.previousSibling; |
| const txt2 = b.firstChild; |
| |
| const observer = new MutationObserver((mutations) => { |
| |
| assert_equals(mutations.length, 3, "mutations.length"); |
| |
| assert_equals(mutations[0].target.id, "a", "Target of the removal"); |
| assert_equals(mutations[0].addedNodes.length, 0, "Should not have added nodes"); |
| assert_equals(mutations[0].removedNodes.length, 1, "Should have 1 removed node"); |
| assert_equals(mutations[0].removedNodes[0], c, "Should have removed c node"); |
| |
| assert_equals(mutations[1].target.id, "a", "Target of the removal"); |
| assert_equals(mutations[1].addedNodes.length, 0, "Should not have added nodes"); |
| assert_equals(mutations[1].removedNodes.length, 1, "Should have 1 removed node"); |
| assert_equals(mutations[1].removedNodes[0], txt, "Should have removed txt node"); |
| |
| assert_equals(mutations[2].target.id, "b", "Target of the replaceChildren"); |
| assert_equals(mutations[2].removedNodes.length, 1, "Should have removed 1 node"); |
| assert_equals(mutations[2].removedNodes[0], txt2, "Should have removed txt2 node"); |
| assert_equals(mutations[2].addedNodes.length, 2, "Should have added a node"); |
| assert_equals(mutations[2].addedNodes[0], c, "Should have added c node"); |
| assert_equals(mutations[2].addedNodes[1], txt, "Should have added txt node"); |
| |
| observer.disconnect(); |
| t.done(); |
| }); |
| |
| observer.observe(a, { |
| subtree: true, |
| childList: true |
| }); |
| |
| b.replaceChildren(c, txt); |
| }, "There should be MutationRecords for the nodes removed from another parent node."); |
| </script> |
| |
| </html> |