Native rendering and state
Clone reusable UI with template
Keep inert task-card markup in a template, clone its content, and fill safe DOM fields when a task is added.
Repeated interface markup does not require a component framework. A template stores a fragment that the browser parses but does not render. Its descendants are inert until you clone and insert its content.
Use one task-card template as the shape for new records. Clone with cloneNode(true), select fields within the clone, assign text through textContent, and set identifiers through known attributes. Insert the completed fragment once to avoid showing half-filled content.
<template id="task-template">
<article class="task" data-task-id="">
<h2></h2>
<button type="button" data-action="complete">Complete</button>
</article>
</template>
<script>
function renderTask(task) {
const fragment = taskTemplate.content.cloneNode(true)
const card = fragment.querySelector(".task")
card.dataset.taskId = task.id
card.querySelector("h2").textContent = task.title
board.append(fragment)
}
</script>
IDs inside a template are copied too. Repeating an ID creates invalid relationships between labels, controls, targets, and selectors. Prefer classes and data hooks, or assign a unique ID and update every related for, aria-*, and target attribute during rendering.
Scripts inside template content do not run while the template is parsed. Avoid relying on script tags inside clones for initialization anyway. A delegated listener on the board already handles repeated actions and keeps behavior separate from the fragment’s data.
Treat template filling as an output boundary. User-provided task titles are text, not markup. textContent prevents a title such as <img onerror=...> from becoming executable HTML. Use trusted DOM construction when rich content is genuinely needed.
Render three records, including a title containing angle brackets, and inspect the DOM. The characters should appear as text. Add a task after page load and complete it through the existing delegated handler. Remove the template element after rendering and confirm existing clones remain ordinary independent DOM nodes. If only the first card updates, look for a document-wide selector or duplicated ID where a clone-local query belongs.
Lesson completed