From d4594fbddefa0efb56eeb9d6f0df489b4221d563 Mon Sep 17 00:00:00 2001 From: Brian Kim Date: Tue, 10 Nov 2020 15:05:33 -0500 Subject: [PATCH] clear DOM nodes when children exist in props (#167) --- src/__tests__/dom.tsx | 13 ++++++ src/dom.ts | 97 ++++++++++++++++++++++--------------------- 2 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/__tests__/dom.tsx b/src/__tests__/dom.tsx index 331bd654f..61f6be430 100644 --- a/src/__tests__/dom.tsx +++ b/src/__tests__/dom.tsx @@ -631,4 +631,17 @@ describe("render", () => { "30px", ); }); + + test("clearing mutations", () => { + renderer.render(
, document.body); + + const div = document.body.firstChild as HTMLDivElement; + div.innerHTML = "child"; + renderer.render(
, document.body); + + expect(document.body.innerHTML).toEqual("
child
"); + renderer.render(
{[]}
, document.body); + + expect(document.body.innerHTML).toEqual("
"); + }); }); diff --git a/src/dom.ts b/src/dom.ts index 568067ee4..cdb307c50 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -9,6 +9,9 @@ import { const SVG_NAMESPACE = "http://www.w3.org/2000/svg"; +// TODO: maybe the HasChildren flag can be defined on (Crank) Element flags... +const HasChildren = Symbol.for("crank.HasChildren"); + export class DOMRenderer extends Renderer { render( children: Children, @@ -156,71 +159,69 @@ export class DOMRenderer extends Renderer { )}`, ); } - if ( - !("innerHTML" in el.props) && - (children.length !== 0 || (node as any).__cranky) + ("children" in el.props || (node as any)[HasChildren]) && + !("innerHTML" in el.props) ) { if (children.length === 0) { node.textContent = ""; - return; - } + } else { + let oldChild = node.firstChild; + let i = 0; + while (oldChild !== null && i < children.length) { + const newChild = children[i]; + if (oldChild === newChild) { + oldChild = oldChild.nextSibling; + i++; + } else if (typeof newChild === "string") { + if (oldChild.nodeType === Node.TEXT_NODE) { + if ((oldChild as Text).data !== newChild) { + (oldChild as Text).data = newChild; + } - let oldChild = node.firstChild; - let i = 0; - while (oldChild !== null && i < children.length) { - const newChild = children[i]; - if (oldChild === newChild) { - oldChild = oldChild.nextSibling; - i++; - } else if (typeof newChild === "string") { - if (oldChild.nodeType === Node.TEXT_NODE) { - if ((oldChild as Text).data !== newChild) { - (oldChild as Text).data = newChild; + oldChild = oldChild.nextSibling; + } else { + node.insertBefore(document.createTextNode(newChild), oldChild); } - oldChild = oldChild.nextSibling; + i++; + } else if (oldChild.nodeType === Node.TEXT_NODE) { + const nextSibling = oldChild.nextSibling; + node.removeChild(oldChild); + oldChild = nextSibling; } else { - node.insertBefore(document.createTextNode(newChild), oldChild); + node.insertBefore(newChild, oldChild); + i++; + // TODO: This is an optimization but we need to think a little more about other cases like prepending. + if (oldChild !== children[i]) { + const nextSibling = oldChild.nextSibling; + node.removeChild(oldChild); + oldChild = nextSibling; + } } + } - i++; - } else if (oldChild.nodeType === Node.TEXT_NODE) { + while (oldChild !== null) { const nextSibling = oldChild.nextSibling; node.removeChild(oldChild); oldChild = nextSibling; - } else { - node.insertBefore(newChild, oldChild); - i++; - // TODO: this is an optimization for the js frameworks benchmark swap rows, but we need to think a little more about other cases like prepending. - if (oldChild !== children[i]) { - const nextSibling = oldChild.nextSibling; - node.removeChild(oldChild); - oldChild = nextSibling; - } } - } - while (oldChild !== null) { - const nextSibling = oldChild.nextSibling; - node.removeChild(oldChild); - oldChild = nextSibling; - } - - for (; i < children.length; i++) { - const newChild = children[i]; - node.appendChild( - typeof newChild === "string" - ? document.createTextNode(newChild) - : newChild, - ); + for (; i < children.length; i++) { + const newChild = children[i]; + node.appendChild( + typeof newChild === "string" + ? document.createTextNode(newChild) + : newChild, + ); + } } + } - if (children.length > 0) { - (node as any).__cranky = true; - } else if ((node as any).__cranky) { - (node as any).__cranky = false; - } + if (children.length > 0) { + (node as any)[HasChildren] = true; + } else if ((node as any)[HasChildren]) { + (node as any)[HasChildren] = false; } } }