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 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 inside the clone, assign text through textContent, and set identifiers through known attributes. Insert the finished fragment once so users never see 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>
<section id="board"></section>
<script>
const taskTemplate = document.querySelector('#task-template')
const board = document.querySelector('#board')
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>
Call renderTask({ id: 'task-18', title: 'Ship checkout' }) and a new card appears with that title in the heading. The template itself never shows on the page.
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. Do not rely on script tags inside clones for initialization. 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 like <img onerror=...> from becoming executable HTML. Use trusted DOM construction when rich content is genuinely needed.
Try this on your board: render three records, including a title with angle brackets, and inspect the DOM. The characters should appear as plain text. Add a task after load and complete it through the existing delegated handler. If only the first card updates, look for a document-wide selector or duplicated ID where a clone-local query belongs.
Lesson completed