Skip to content

Recursive fragment w/ lost lastChild during target update #102

@lazex

Description

@lazex

I get an error in complex cases.

<!DOCTYPE html>
<meta charset="utf-8" />

<script type="module">
	import { render, html } from "https://cdn.jsdelivr.net/npm/uhtml@4.3.4/index.js"

	const tpl = (tree, state) => {
		const item = tree.find(x => x.name === state[0])
		return html`
			<div>
				${tree.map(item => html`<span>${item.name}</span>`)}
			</div>
			${item?.children
				? tpl(item.children, state.slice(1))
				: null}
		`
	}

	const tree = [
		{
			name: "A",
			children: [
				{
					name: "A-A",
					children: [
						{
							name: "A-A-A",
							children: [
								{ name: "A-A-A-A" }
							],
						},
					],
				},
			],
		},
		{
			name: "B",
			children: [{ name: "B-A" }],
		},
	]

	const case1 = () => {
		const update = (state) => render(root1, tpl(tree, state))

		update(["A"])
		update(["A", "A-A"])
		update(["A", "A-A", "A-A-A"])
		update(["B"]) // ERROR
	}
	const case2 = () => {
		const update = (state) => render(root2, tpl(tree, state))

		update(["A"])
		update(["A", "A-A"])
		// update(["A", "A-A", "A-A-A"])
		update(["B"]) // NO ERROR
	}
	const case3 = () => {
		const update = (state) => render(root3, tpl(tree, state))

		// update(["A"])
		// update(["A", "A-A"])
		update(["A", "A-A", "A-A-A"])
		update(["B"]) // NO ERROR
	}

	for (const case_ of [case1, case2, case3]) {
		try {
			case_()
		} catch (err) {
			console.error(`[${case_.name} error]`, err)
		}
	}
</script>

<div id="root1"></div>
<div id="root2"></div>
<div id="root3"></div>

case1 gives the following error.

DOMException: Failed to execute 'setEndAfter' on 'Range': the given Node has no parent.

However, it does not occur in case2 and case3.

This can be avoided by wrapping the top level of the html returned by the tpl function in a div.

const tpl = (tree, state) => {
	const item = tree.find(x => x.name === state[0])
	return html`
		<div>
			<div>
				${tree.map(item => html`<span>${item.name}</span>`)}
			</div>
			${item?.children
				? tpl(item.children, state.slice(1))
				: null}
		</div>
	`
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions