A small app, end to end
Everything at once: a to-do list that reads a field, listens for a submit, builds list items and keeps the page in step.
Everything, in one small app
A to-do list is the smallest thing that uses the whole language: a value from a field, a decision about empty input, a new element built and appended, and an event that starts it all. No framework, no build step — the ideas from this course, in order.
It is also worth building once by hand. Every framework you meet afterwards is a tidier way of writing these same five steps, and recognising them is what makes the tidier version make sense.
The order matters as much as the pieces. Cancel first, read second, decide third, build fourth, place fifth, clear last. A step out of order produces a specific symptom: clearing before reading empties the field, and appending before deciding adds the blank row you meant to reject.
The checklist for this app
- The form listens for
submit, and the page is stopped from reloading. - The field’s value is read and trimmed.
- Empty input is ignored, with nothing built and nothing added.
- A list item is created and its text set from the value.
- The item is appended to the list.
- The field is cleared, ready for the next entry.
Keep the list, draw the list
The important decision in a small app is where the truth lives. Keep the to-dos in an array and treat the page as a drawing of that array. Adding an entry changes the array, then a render function clears the list and builds every item again from the current data.
That sounds slower than appending one new item, and it is a little. What it buys is agreement: the page cannot show an item the array does not have, or miss one it does. A count and an empty-state message are then derived from the same array, so they cannot disagree either.
A worked submit, end to end
Suppose the visitor types buy bread and presses Add. The submit event fires, the handler calls preventDefault, and the page stays put. The handler reads input.value, trims it to buy bread, and finds it non-empty. It creates an li, sets its text, appends it to the list, and clears the field. The list now has one more row and the field is ready for the next entry.
Type spaces only and press Add: the trimmed value is empty, so the guard returns before anything is built. That early return is what keeps blank rows out of the list. Without it, the same steps would append an empty item, and the visitor would have to delete it by hand.
Reading the finished app
When the app misbehaves, read it in the order it runs. Was the form found? Did the listener attach? Did the handler fire? What did the field contain after trimming? Was an item created and appended? Each question is a console.log away, and the first one that gives an unexpected answer is the bug.
That habit scales to larger programs. The page is a sequence of small transformations, and a bug is a place where one transformation received something other than what the previous one produced. Logging the value at the seam is faster than reading the whole file again.
Three ways a small app drifts: list.innerHTML += ... reparses every existing item and throws away its listeners; appending to the page but not to the array leaves the two out of step on the next render; and appending the same element reference twice moves it instead of copying it, so an item vanishes from where you expected it.
The to-do list, finished
Wire the form: stop the page reloading, read the field, ignore an empty entry, build a list item and append it, then clear the field.
4 left to do
- The form listens for submit
- The page is stopped from reloading
- A list item is created
- It is appended to the list
Listen for submit on the form; event.preventDefault(); read input.value.trim(); document.createElement("li"); list.append(item); input.value = "".
The same list, drawn from state
Keep the entries in an array, render the whole list from it with a loop, show how many there are, and let the clear button empty both the array and the page.
3 left to do
- The entries live in an array
- The list is drawn with a loop
- The list can be emptied
Start const todos = [];; a render function does list.replaceChildren(), then a forEach that builds the items; the clear button sets todos.length = 0 and renders again.
Try these next
- Add a delete button to every item and remove that entry from the array before rendering again.
- Show "Nothing yet" in the list when the array is empty.
- Reject an entry that is only spaces, and say why in the message line.
Where to go next
You have the language now: values, decisions, loops, functions, arrays and objects, the DOM, events, forms, templates, dialogs and your own elements. What is left is mostly library knowledge — the same ideas applied to problems that come with names.
Three directions worth knowing about. Modules split a large program into files that import from each other. fetch asks a server for data and hands it back as JSON — the same loads idea from Python, over the network. And a framework is a tool for building interfaces that change often; every one of them is built on the DOM, events and state you have just used by hand.
The way forward is a small app you actually want — a habit tracker, a recipe box, a reading list. Keep the checklist beside it, and read other people’s code when it confuses you.
The best next program is one you want to exist. Want is what makes you finish it, and finishing is what teaches.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
Which order is right for handling a form submission?
