Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

addEventListener
A call on an element that registers a listener for one kind of event.
closest
A way to find the nearest parent of an element that matches a rule you give, starting with the element itself.
closure
A function together with the variables from the place it was written, which stay available when it runs later.
default behaviour
What the browser does on its own when an event happens, such as following a link or submitting a form, before any script intervenes.
DOM
The tree of objects a browser builds from the page, which a script can read and change while the page is open.
element
One item in the page tree, such as a heading or a button, which a script can find and change.
event
Something that happens on the page, such as a click, a key press, or a form being submitted, which a script can respond to.
event bubbling
The way an event rises from the element that received it up through each of its parents.
event target
The element that actually received the action, which may be a child of the element whose listener is running.
function
A reusable block of code that can be called with values and can return an answer to whoever called it.
handler
The function you give to a listener, which the browser runs when the event it waits for happens.
listener
A function registered on an element to wait for one kind of event and run each time that event reaches it.
property
A named value stored on an object, usually read and written with a dot, such as the text of an element.
reference
A value that points at an object rather than holding a copy, so a change made through one is visible through the other.
scope
The part of a program where a name can be used, decided by where it was declared.
variable
A name that holds a value so you can use it again; a constant cannot be pointed at a different value later.

JavaScript from the ground up

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.

Part 9 of 12

Advanced16 min readBack to Courses

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 of showModal() and compare the backdrop and focus behaviour.
  • Log dialog.returnValue inside the close handler 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().

Live Preview
Console output will appear here...

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.

Live Preview
Console output will appear here...

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?