Templates and dialogs
Markup you can stamp out and a modal the browser operates: clone a template, fill it, and open it as a dialog.
Markup that waits
A <template> holds markup the browser parses and never draws. It is a pattern for JavaScript to copy when it needs one — a row, a card, a note — instead of building the structure element by element in code.
The copy is made with template.content.cloneNode(true): a deep, independent clone you can fill in and put on the page. The true means every descendant comes too, which is almost always what you want.
The browser parses the template’s contents when it reads the page, so malformed markup inside it is still a parse error, but nothing is rendered and no images inside it are loaded. That is why a template is a better pattern than a hidden div: hidden content still takes part in the page in ways you have to remember to undo.
Clone, fill, place
const template = document.querySelector("#note-template");
const copy = template.content.cloneNode(true);
copy.querySelector(".note-text").textContent = "Baked at 07:00";
document.querySelector(".notes").append(copy);A dialog the browser operates
The <dialog> element is a modal window with the hard parts built in. dialog.showModal() opens it and the browser raises a backdrop, moves focus inside and wires Escape to close it; dialog.close() closes it from your own buttons.
That is why it beats a div styled to look like a modal: the keyboard behaviour, the focus trap and the dimming all come free, and they are the parts a hand-made version usually gets wrong.
The built-in behaviours are the ones that are hard to reproduce: focus moves into the dialog and cannot tab out to the page behind it, Escape closes it, and the rest of the page is inert. A div with a high z-index gives you none of that, and adding it by hand is a project of its own.
The two together
A dialog wants content, and a template is the cleanest way to supply it: clone the pattern, fill in the values for this visitor, append the copy inside the dialog and open it. The dialog element itself stays empty in the HTML — which is also why the page reads sensibly before any script runs.
A template is a fragment, not an element
The markup inside a <template> does not live as ordinary children of the template. It sits in template.content, a DocumentFragment that the browser keeps out of the document. That is why template.querySelector(".note-text") finds nothing — you search the clone, after making it.
A clone is independent: changing the copy’s text leaves the pattern untouched, and cloning again gives a fresh copy of the original. Append the fragment once and it becomes part of the page; the same clone appended a second time would move, not duplicate, so make a new clone per use.
A dialog has its own events
showModal() opens the dialog as a modal; show() opens it as an ordinary box with no backdrop and no focus trap. When the dialog closes — by its own button, by Escape, or by a form inside it — a close event fires, and dialog.returnValue holds the value of whatever closed it.
A <form method="dialog"> inside the dialog is the tidy way to ask a question: submitting it closes the dialog and sets returnValue to the value carried by the button that submitted. That is how a two-button choice becomes one short close handler.
A worked clone, step by step
Suppose the template holds one paragraph with the class note-text. template.content.cloneNode(true) returns a fragment containing a copy of that paragraph. Searching the copy with .querySelector(".note-text") finds the paragraph, and setting its text changes only the copy. Appending the fragment to .notes moves its children into the page.
Clone again and you get a second, independent paragraph with the template’s original text. That is the pattern for a list: one template, one clone per row, each filled with its own values. The template itself is never appended and never changes.
Three mistakes: searching the template itself instead of the clone, which returns null; appending the same clone twice, which moves it rather than making a second copy; and reaching for show() when the dialog should trap focus and dim the page — that is showModal().
Try these next
- Change the template’s text, then clone it twice and confirm both copies differ from the pattern.
- Open the dialog with
show()instead ofshowModal()and compare the backdrop and focus behaviour. - Log
dialog.returnValueinside theclosehandler after each button.
Open the note
The button, the dialog and the template are already in the page. Wire them: clone the template into the dialog and open it with showModal().
3 left to do
- The button listens for a click
- The template is cloned
- The dialog opens with showModal()
Listen for a click on the button, template.content.cloneNode(true), append the copy into the dialog, then dialog.showModal().
A dialog that returns an answer
Open the dialog from the button, then read the answer when it closes. The dialog has two submit buttons carrying different values; show which one was pressed in the paragraph.
3 left to do
- The dialog opens with showModal()
- The close event is handled
- The returned answer is read
Listen for click on the open button and call showModal(). Listen for close on the dialog and read dialog.returnValue.
A <form method="dialog"> inside the dialog closes it on submit, returning whatever value the button carried — the tidy way to ask a question and get an answer.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does a <template> do when the page loads?
