Back to Follow the Leader Under the Hood  •  JavaScript Explained for Novices
Follow the Leader • Under the Hood • For Novices

It Looks Like a Form.
So Why Is It a JavaScript App?

Follow the Leader looks simple — fill in some boxes, click a button. But underneath, JavaScript is doing five distinct jobs that HTML alone is completely incapable of. Here’s what’s really going on.

The big picture first

Every webpage is actually three languages at once

HTML

Structure. The skeleton. Headings, paragraphs, tables, buttons. HTML describes what exists on a page — but it cannot react to anything you do.

CSS

Style. The paint and furnishings. Colours, fonts, spacing, layout. CSS makes things look good — but it cannot remember anything or respond to clicks.

JavaScript

Behaviour. The brain. JavaScript watches what you do, reacts to it, stores data, builds new elements, and talks to the browser — all in real time.

A quick analogy
Imagine a school. HTML is the building — walls, desks, chairs. CSS is the decoration — paint colours, carpet, signs on the doors. JavaScript is the staff — the people who unlock the doors when you arrive, write your name in the register, and remember what you did yesterday. Without JavaScript, the building exists and looks nice, but nothing actually happens.
What Follow the Leader would be without JavaScript: A static form that forgets everything the moment you refresh the page. No auto-saving. No auto-growing rows. No Export Summary. No Print button logic. No “Clear Trail” confirmation. Just a pretty, useless box.
Five JavaScript jobs running inside this app

What JavaScript is actually doing — right now

1
Building the trail table from scratch
DOM Manipulation

Open the HTML source of this page and search for the trail table. You will find this:

<tbody id="ftlTableBody"> <!-- Rows injected by followTheLeaderScripts.js --> </tbody>

That’s it. The table is completely empty in the HTML file. Every row — the six starter rows, and every row that appears when you type — is manufactured on the fly by JavaScript.

The script uses a function called buildRow() that constructs a brand-new table row in memory, fills it with inputs, attaches behaviours to those inputs, and then drops the finished row into the page. HTML describes fixed content. JavaScript creates content dynamically, based on what’s needed at the time.

The core browser tool for this is the DOM — the Document Object Model. Think of the DOM as a live map of every element on the page. JavaScript can read this map, add to it, change it, and delete from it at any moment.

// Create a brand-new <tr> element in memory var tr = document.createElement('tr'); // Create a cell and an input inside it var tdLoc = document.createElement('td'); var locIn = document.createElement('input'); locIn.type = 'text'; locIn.placeholder = 'Page name or URL…'; // Nest them: input → cell → row tdLoc.appendChild(locIn); tr.appendChild(tdLoc); // Drop the finished row into the actual page tableBody.appendChild(tr);
2
Listening and reacting to what you do
Event Listeners

HTML buttons do nothing on their own. A <button> tag just draws a button. JavaScript is what gives it a job. The app attaches an event listener to every interactive element — it’s like hiring a guard who watches one specific thing and springs into action the moment it changes.

Every keystroke you type in any field, every button click, even the modal opening — each of those actions triggers a specific JavaScript function that was assigned to it using addEventListener.

// "When the Save button is clicked, run saveData()" btnSave.addEventListener('click', function () { saveData(true); }); // "When any input field changes, schedule an auto-save" locIn.addEventListener('input', scheduleAutoSave); // "When the Print button is clicked, open the print dialog" btnPrint.addEventListener('click', function () { window.print(); });

Without event listeners, every button on the page would be decoration. With them, the page becomes genuinely interactive — it does different things depending on what you do, in real time.

3
Remembering your work between visits
localStorage (Web Storage API)

This is the job that most surprises beginners. Close this tab and re-open it. Your name, period, date, quest, and all your trail stops are still there. That is not magic — it’s localStorage.

localStorage is a tiny key–value storage system built into every browser. JavaScript can write data into it and read it back out later. The data survives page refreshes and even closing and reopening the browser — until the user deliberately clears it.

The app saves everything as a single structured blob of text called JSON (JavaScript Object Notation). JSON is just a tidy way to package multiple pieces of data as one string so they can be stored together and unpacked later.

// Package all the form fields into one object var data = { name: getVal('ftlName'), period: getVal('ftlPeriod'), quest: getVal('ftlQuest'), rows: collectRows() // array of trail stops }; // Convert the object to a string and store it in the browser localStorage.setItem('ftl_v1', JSON.stringify(data)); // --- Later, when the page loads again --- var raw = localStorage.getItem('ftl_v1'); var d = JSON.parse(raw); // convert the string back to an object setVal('ftlName', d.name); // restore each field

This is exactly the same concept that games use to save progress, that shopping sites use to remember your cart, and that Google Docs uses to auto-save every few seconds. The scale is different; the idea is identical.

4
Growing the table automatically as you fill it
Conditional Logic + Dynamic Insertion

The trail table starts with six rows. The moment you begin typing in the location field of the very last row, three new rows appear below it. You never have to click “Add” for this to happen — JavaScript is watching.

Every time you type a character in a location field, the script checks a question: “Is this the last row in the table right now?” If the answer is yes and you’ve typed at least one character, it calls growTable(3) to append three more rows immediately.

// Attached to every location input when its row is built locIn.addEventListener('input', function () { scheduleAutoSave(); var allRows = tableBody.querySelectorAll('tr'); // Is THIS row the last one, and does it have content? if (tr === allRows[allRows.length - 1] && locIn.value.trim()) { growTable(3); // append three new empty rows } });

This is a pattern called conditional logic — the code makes a decision (if some condition is true, do something). It’s arguably the most fundamental idea in all of programming. Every app ever written — from a calculator to a self-driving car — is ultimately a large collection of “if this, then that” decisions.

5
Building the Export Summary and copying it
String Assembly + Clipboard API

Click Export Summary on the main page. A neatly formatted plain-text trail report appears instantly, ready to be copied and pasted anywhere. That text does not exist anywhere in the HTML or CSS files. JavaScript assembles it on demand, piece by piece, from whatever is currently typed into the form.

The script reads the value of every field, formats them into labelled lines, loops through every trail row collecting non-empty stops, and joins everything into a single multi-line string. The Clipboard API then lets JavaScript reach into the operating system and place that string on your clipboard — exactly as if you had manually selected the text and pressed Ctrl+C.

// Build the summary text by assembling pieces var out = []; out.push('TNT Follow the Leader — Trail Log'); out.push('Name: ' + document.getElementById('ftlName').value); out.push('Quest: ' + document.getElementById('ftlQuest').value); // ... loop through trail rows ... var finalText = out.join('\n'); // join lines with newline // Send the finished text to the clipboard navigator.clipboard.writeText(finalText).then(function () { btn.textContent = 'Copied!'; // confirm to the user });

Notice that last part — after copying, the button label changes to “Copied!” for a couple of seconds before reverting. That’s JavaScript updating the DOM (Job 1) in response to an event (Job 2), triggered by an API call. All five jobs can be in play at once.

Bonus concept — debouncing: The app auto-saves 900 ms after you stop typing, not on every keystroke. That technique is called debouncing. Without it, JavaScript would call localStorage.setItem hundreds of times per second while you type. Debouncing uses setTimeout and clearTimeout to restart a countdown on every keystroke, only running the save when you pause. It’s a tiny optimisation that every professional JavaScript developer uses constantly.
Bonus concept — console breadcrumbs: The “breadcrumbs” in the trail form have a direct cousin in CS: console.log(), a one-line statement that prints a message to DevTools the moment a function runs. This app has deliberate breadcrumbs in every major function — open DevTools (F12) → Console tab, reload the page, and you’ll see the startup trace: ...loadData, ...initTable: 6 rows. Type in a field and pause: ...autoSave fired (900ms idle) appears once, showing the debounce in action. That is a live execution trace of everything the program just did. See Ask Copilot Entry #039 for the full story. Build this habit now — before you have a bug to find.
Vocabulary you now know

Terms a beginner JS programmer uses every day

DOM Document Object Model — the live map of every element on the page that JavaScript can read and rewrite.
Event listener Code that watches a specific element for a specific action (click, input, keydown) and runs a function when it happens.
localStorage A key–value store built into the browser. Data persists across refreshes and sessions until cleared.
JSON JavaScript Object Notation — a standard text format for packaging structured data so it can be stored or transmitted as a single string.
Conditional logic An if / else statement. The engine of every decision a program makes.
Debouncing Delaying an action until a burst of rapid events has settled, preventing unnecessary repeated execution.
Clipboard API A browser interface that lets JavaScript read from and write to the operating system clipboard programmatically.
Function A named, reusable block of code. You define it once; you can call it anywhere, as many times as you need.
console.log() Prints a message to DevTools Console the moment a line of code executes. Used as a “breadcrumb” to trace program execution and catch bugs before they get away.
The bigger picture

You just read the source code of a real app

Every feature on this page — the auto-saving, the growing table, the export modal, the clipboard copy — is explained entirely by about 200 lines of JavaScript. No back-end server. No database. No programming language other than the one that ships in every browser on the planet, for free, by default.

That is what makes JavaScript remarkable. You could open DevTools right now (F12 on most browsers), click the Sources tab, and read every line that powers this app. Nothing is hidden. Everything you learned on this page is sitting there, legible, available to study, copy, and remix.

The five jobs you just read about — DOM manipulation, event listeners, localStorage, conditional logic, and the Clipboard API — are not beginner topics that you’ll throw away when you get serious. They are the core vocabulary of professional front-end development. Every JavaScript developer in the industry uses all five of them, every day.

Try It Yourself — Five DevTools Experiments

  1. Press F12 to open DevTools. Go to Application → Local Storage. Fill in your name on the trail form and watch the ftl_v1 entry update in real time as you type.
  2. In the Console tab, type localStorage.getItem('ftl_v1') and press Enter. You’ll see the raw JSON string that holds your entire trail.
  3. In the Sources tab, open followTheLeaderScripts.js. Find the buildRow function. Read it — you now understand every line of it.
  4. In the Console, type document.getElementById('ftlName').value = 'Sherlock Holmes' and press Enter. Watch the Name field change. You just wrote JavaScript that manipulates the DOM.
  5. In the Elements tab, find <tbody id="ftlTableBody">. Expand it. Those <tr> elements weren’t in the original HTML — JavaScript built every one of them.
  6. Stay in the Console tab and click Test Data. Watch the breadcrumb trace: ...loadTestData...btnSave clicked...saveData. Then click Print and watch ...beforeprint report how many inputs were substituted with flow divs. This is the program narrating its own execution — exactly what console.log() is for.