diff --git a/cjs/mixin/child-node.js b/cjs/mixin/child-node.js index 364c23d0..4d6f8e51 100644 --- a/cjs/mixin/child-node.js +++ b/cjs/mixin/child-node.js @@ -16,23 +16,34 @@ const asFragment = (ownerDocument, nodes) => { return fragment; }; +// `node` itself can be one of `nodes`, so the insertion point is the nearest +// sibling that is not moved (the spec's viable previous/next sibling). const before = (node, nodes) => { const {ownerDocument, parentNode} = node; - if (parentNode) + if (parentNode) { + let prev = node.previousSibling; + while (prev && nodes.includes(prev)) + prev = prev.previousSibling; + const fragment = asFragment(ownerDocument, nodes); parentNode.insertBefore( - asFragment(ownerDocument, nodes), - node + fragment, + prev ? prev.nextSibling : parentNode.firstChild ); + } }; exports.before = before; const after = (node, nodes) => { const {ownerDocument, parentNode} = node; - if (parentNode) + if (parentNode) { + let next = getEnd(node)[NEXT]; + while (next && nodes.includes(next)) + next = getEnd(next)[NEXT]; parentNode.insertBefore( asFragment(ownerDocument, nodes), - getEnd(node)[NEXT] + next ); + } }; exports.after = after; diff --git a/esm/mixin/child-node.js b/esm/mixin/child-node.js index ec8b55eb..a9a968d5 100644 --- a/esm/mixin/child-node.js +++ b/esm/mixin/child-node.js @@ -15,22 +15,33 @@ const asFragment = (ownerDocument, nodes) => { return fragment; }; +// `node` itself can be one of `nodes`, so the insertion point is the nearest +// sibling that is not moved (the spec's viable previous/next sibling). export const before = (node, nodes) => { const {ownerDocument, parentNode} = node; - if (parentNode) + if (parentNode) { + let prev = node.previousSibling; + while (prev && nodes.includes(prev)) + prev = prev.previousSibling; + const fragment = asFragment(ownerDocument, nodes); parentNode.insertBefore( - asFragment(ownerDocument, nodes), - node + fragment, + prev ? prev.nextSibling : parentNode.firstChild ); + } }; export const after = (node, nodes) => { const {ownerDocument, parentNode} = node; - if (parentNode) + if (parentNode) { + let next = getEnd(node)[NEXT]; + while (next && nodes.includes(next)) + next = getEnd(next)[NEXT]; parentNode.insertBefore( asFragment(ownerDocument, nodes), - getEnd(node)[NEXT] + next ); + } }; export const replaceWith = (node, nodes) => { diff --git a/test/html/element.js b/test/html/element.js index 3c3fedd8..bf6ca456 100644 --- a/test/html/element.js +++ b/test/html/element.js @@ -35,6 +35,36 @@ node.replaceWith(amp); assert(amp.wholeText, 'before&after'); assert(document.toString(), 'before&after

ab', '.before() and after()'); +{ + const {document} = parseHTML('
ab
'); + const div = document.firstChild; + const i = div.querySelector('i'); + i.after('x', i); + assert(div.outerHTML, '
axb
', '.after() with itself among the nodes'); + i.before(i, 'y'); + assert(div.outerHTML, '
axyb
', '.before() with itself among the nodes'); + const b = div.lastChild; + i.after(b, 'z'); + assert(div.outerHTML, '
axbzy
', '.after() moving the next sibling'); + i.before(div.firstChild, i); + assert(div.outerHTML, '
xabzy
', '.before() moving the first child and itself'); + i.before(i.previousSibling, 'p'); + assert(div.outerHTML, '
xapbzy
', '.before() moving the previous sibling'); + i.after(i.nextSibling, 'n'); + assert(div.outerHTML, '
xapbnzy
', '.after() moving the next sibling first'); +} + +{ + const {document} = parseHTML('

ac

'); + const p = document.firstChild; + const b = p.querySelector('b'); + b.before(b.previousSibling); + b.after(b.nextSibling); + b.before(b, p.lastChild); + b.after(p.firstChild, b); + assert(p.outerHTML, '

ac

', '.before() and .after() without a viable sibling'); +} + let div = document.createElement('div'); let rect = div.getBoundingClientRect();