diff --git a/packages/solid/web/src/index.ts b/packages/solid/web/src/index.ts index ba07a58cb..0d2d44e82 100644 --- a/packages/solid/web/src/index.ts +++ b/packages/solid/web/src/index.ts @@ -101,7 +101,7 @@ export function Portal(pro insert(renderRoot, content); el.appendChild(container); props.ref && (props as any).ref(container); - onCleanup(() => el.removeChild(container)); + onCleanup(() => el.contains(container) && el.removeChild(container)); } }, undefined, diff --git a/packages/solid/web/test/portal.spec.tsx b/packages/solid/web/test/portal.spec.tsx index 9cae05b1c..c234c6a11 100644 --- a/packages/solid/web/test/portal.spec.tsx +++ b/packages/solid/web/test/portal.spec.tsx @@ -115,6 +115,24 @@ describe("Testing a Portal with Synthetic Events", () => { test("dispose", () => disposer()); }); +describe("Testing a Portal whose mount is externally cleared before disposal", () => { + let div = document.createElement("div"), + disposer: () => void; + const testMount = document.createElement("div"); + const Component = () => Hi; + + test("Create portal", () => { + disposer = render(Component, div); + expect((testMount.firstChild as HTMLDivElement).innerHTML).toBe("Hi"); + }); + + test("dispose does not throw when mount has been externally emptied", () => { + // Simulate an external actor clearing the mount node (e.g. innerHTML = "") + testMount.innerHTML = ""; + expect(() => disposer()).not.toThrow(); + }); +}); + describe("Testing a Portal with direct reactive children", () => { let div = document.createElement("div"), disposer: () => void,