Courses Learn Blog Contact
Get Started →
Lesson 5

Day 5: Project 1 - Make It Work

The first solo project: a task application built from a problem statement, with the ten requirements, the marking rubric and the Friday defence.

Webbo3 Level 2: JavaScript Development 11 min read Free

Webbo3 Level 2 · JavaScript Development · Day 5 of 20

Project 1: Make It Work

Your first solo build. A problem statement, not a tutorial, and a Friday where you defend what you wrote.

By the end of today you can

  • Turn a problem statement into a working application without being given steps
  • Keep your data in one array and draw the page from it
  • Find the right item by its id, and avoid the string-versus-number trap
  • Push your work to GitHub with a README somebody else could follow
  • Demonstrate it live and explain any line you are asked about

There is no tutorial today. You are given a problem and a standard, and the gap between them is the work.

This is deliberate. Copying a tutorial teaches you to copy tutorials. The first time you face a blank file with nobody to copy is the first time you find out whether you can actually do this. Better that it happens here, on a Friday, with your instructor in the room.

Before the brief, two ideas the whole project stands on. Understand these and the ten requirements become ten small jobs instead of one big fog.

1. One Array of Truth, One render()

Definition

Rendering from data means your tasks live in one JavaScript array, and one function, render(), draws the page from that array. Every action (add, complete, delete) changes the array and then calls render() again. You never read the page to find out what tasks exist.

Real-Life Example

A restaurant keeps its orders in the order book, and the kitchen screen shows what the book says. When an order changes, the waiter changes the book, and the screen redraws from it. Nobody walks over to the screen and squints at it to work out what the orders are.

The array is the order book. The page is the screen. You saw this on Day 4 in the final scenario; today the whole project is built this way.

How It Works

  1. State: the array of tasks, plus the current filter and search text.
  2. Derived data: a function such as visibleTasks() that works out which tasks should be on screen right now. It never touches the page.
  3. Render: render() draws exactly what visibleTasks() returns.
  4. Actions: every click or keystroke changes the state, then calls render.

Example 1: Deciding What Is Visible

toLowerCase() turns text into small letters, so a search for "GitHub" still matches "github". includes() asks whether one piece of text appears inside another.

What this code doesWe want to work out which tasks belong on screen for any filter and search, without touching the page at all.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// The state: the tasks, the current filter and the search text
const tasks = [
  { id: 1, text: 'Finish Day 4 exercise', done: true },
  { id: 2, text: 'Push project to GitHub', done: false },
  { id: 3, text: 'Read the Day 5 brief', done: false }
];
let filter = 'all';   // 'all', 'active' or 'completed'
let search = '';

// Does a task pass the current filter?
function matchesFilter(task) {
  if (filter === 'active') return !task.done;
  if (filter === 'completed') return task.done;
  return true;
}

// Does a task contain the search text (ignoring capitals)?
function matchesSearch(task) {
  return task.text.toLowerCase().includes(search.toLowerCase());
}

// Which tasks should be on screen right now?
function visibleTasks() {
  return tasks.filter(matchesFilter).filter(matchesSearch);
}

// Try it with different filters and searches
console.log('all:', visibleTasks().length);

filter = 'active';
console.log('active:', visibleTasks().map((task) => task.text));

search = 'github';
console.log('active + "github":', visibleTasks().map((task) => task.text));
OUTPUT   TERMINAL
➜ webbo3 node app.js
all: 3
active: [ 'Push project to GitHub', 'Read the Day 5 brief' ]
active + "github": [ 'Push project to GitHub' ]

visibleTasks() answers one question: given the filter and search right now, which tasks should be visible? Separating "what is true" from "what is drawn" is the most useful structural idea in this project, and it is exactly what React will do for you in Level 3.

Example 2: The Render Loop on a Page

A Small New Piece of Syntax task.done ? 'done' : '' is the ternary: a one-line if/else that gives back a value. You will see it in the next example. Read it as "if task.done, then 'done', otherwise nothing". Use it only for short choices like this; anything longer belongs in an if.
What this code doesWe want to see the loop working on a real page: the data drawn, a click that changes the data, and the page redrawn. The picture shows it after completing one task.
index.html
1
2
<ul id="list"></ul>
<p id="left"></p>
style.css
1
.done { text-decoration: line-through; color: #888; }
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// State: the one array of truth
const tasks = [
  { id: 1, text: 'Read the brief', done: false },
  { id: 2, text: 'Build the render loop', done: false }
];

const list = document.querySelector('#list');
const left = document.querySelector('#left');

// Draw the page from the array, every time
function render() {
  list.innerHTML = tasks
    .map((task) => {
      const cls = task.done ? 'done' : '';
      return `<li class="${cls}" data-id="${task.id}">${task.text} <button>Toggle</button></li>`;
    })
    .join('');

  // A derived value: never stored, always worked out
  const remaining = tasks.filter((task) => !task.done).length;
  left.textContent = `${remaining} left`;
}

// One listener: change the ARRAY, then redraw
list.addEventListener('click', (event) => {
  if (!event.target.closest('button')) return;
  const id = Number(event.target.closest('li').dataset.id);
  const task = tasks.find((item) => item.id === id);
  task.done = !task.done;
  render();
});

render();
BROWSERindex.html · after toggling the first task
Common Mistake: Treating the Page as the Truth

Reading the list back off the page ("how many li elements are there?") to decide what the tasks are works until the filter hides some of them. Then the page no longer shows every task, and your count, your search and your delete all go wrong at once. The array always knows; the page only shows.

Picture This

Picture the order book and the kitchen screen. Every change goes into the book first, and the screen is simply redrawn from the book. If the screen and the book ever disagree, the book is right.

2. Every Task Needs an id

Definition

An id is a number (or text) that belongs to one task and never changes. You stamp it onto the task's element with a data-id attribute, and when that element is clicked you read it back with dataset.id to find the right task in the array.

Real-Life Example

A cloakroom ticket. The attendant does not remember that your coat is "the third one from the left", because coats come and go and the positions change. Your ticket number stays the same, so it always finds your coat.

Why Not the Position in the Array?

The position (index) of a task changes the moment anything before it is deleted. Use the index and you will eventually delete the wrong task. The id never moves.

Common Mistake: dataset Always Gives You Text

Everything read from dataset is a string. Your ids in the array are numbers, and === checks the type too (Day 1). So without a conversion, the task is never found.

What this code doesWe want to see what happens when an id read from the page is compared with the ids in the array without converting it first.
index.html
1
2
3
<ul id="list">
  <li data-id="2">Push project to GitHub <button>Find me</button></li>
</ul>
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// The array, with ids as numbers
const tasks = [
  { id: 1, text: 'Finish Day 4 exercise' },
  { id: 2, text: 'Push project to GitHub' }
];

document.querySelector('#list').addEventListener('click', (event) => {
  // Read the id back from the clicked row
  const raw = event.target.closest('li').dataset.id;

  // Without converting: '2' === 2 is false
  console.log('without Number():', tasks.find((task) => task.id === raw));

  // With Number(): 2 === 2 is true
  console.log('with Number():', tasks.find((task) => task.id === Number(raw)));
});
BROWSERindex.html · after clicking Find me
CONSOLE   BROWSER DEVTOOLS
without Number(): undefined
with Number(): {id: 2, text: Push project to GitHub}

That is Day 1 coming back: "2" and 2 are different types. It will cost somebody in your cohort an hour today. Always wrap it: Number(element.dataset.id).

Picture This

Picture the cloakroom: the ticket number goes on the coat hanger (data-id), and you hand the same number back to collect it. Just remember the ticket is printed as text, so read it as a number before you compare.

3. The Brief

Project 1 · Solo · One day

Build a personal task and productivity application that lets a person create, complete, edit, delete, filter and search their tasks. It runs entirely in the browser. No backend, no framework, no library.

That is the entire brief, and it is meant to feel slightly too open. Decide the rest yourself: what a task looks like, what happens to a completed one, what the empty state says. Those decisions are part of what is assessed.

What It Must Do

#RequirementProves you understood
1Add a task from a text fieldEvents, form handling, preventDefault
2Refuse an empty task with a visible messageValidation, falsy values
3Mark a task complete, and uncomplete againChanging state, re-rendering
4Edit the text of an existing taskFinding one item by id, updating it
5Delete a taskEvent delegation, closest, ids
6Filter: all, active, completedfilter, re-rendering from data
7Search tasks as the user typesThe input event, filter
8A count of what is leftDerived values
9An empty state when there is nothing to showThinking about the user
10Works on a phone-width screenLast month's CSS, still your job
The One Rule About How You Build It

One array of task objects, one render() function that draws it, and every action changes the array and calls render() again. That is section 1 of today, and it is worth 20 marks on its own.

4. What to Hand In

  • A public GitHub repository with real commit messages. Not one commit called "final": at least six, each describing what changed.
  • A README with: what it does, a screenshot, how to run it, and one honest paragraph on what you would improve with more time.
  • The folder structured the way you learned last month: index.html at the root, css/ and js/.
  • A deployed link if you can manage it. GitHub Pages is free and takes ten minutes. Not required today, required from Day 18.
What this code doesWe want a README that tells a stranger what the project is, what it looks like, how to run it and what you would do next, in under a minute of reading.
README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Task Tracker

A browser-only task list: add, complete, edit, delete, filter and search tasks.

![Screenshot](screenshot.png)

## How to run it

Open index.html in any browser. Nothing to install.

## What I would improve

Tasks disappear on refresh because they only live in memory.
With more time I would save them in the browser (Day 11).
NOTE   
GitHub reads this file and shows it on the front page of your repository.

5. The Friday Evaluation

You do not submit and go home. You demonstrate, and you answer questions. Fifteen minutes each.

PartWhat happensMarks
DemoYou drive. Show every one of the ten requirements working.30
Code walkthroughOpen your JavaScript and explain how the render loop works.20
The bug storyDescribe one bug you hit, how you found it, and how you fixed it.15
Three questionsTechnical questions on your own code, picked live.20
Repo and READMECommit history, structure, and a README that helps.15
The Rule That Governs Everything From Here

Never submit code you cannot explain. If you cannot say what a line does and why it is there, it does not belong in your project yet. This applies equally to code you found online and code an AI wrote for you.

The Kind of Question You Will Be Asked

  • Show me where the task actually gets deleted. Why does that work for a row you added thirty seconds ago?
  • You used === everywhere. What would break if you swapped them for ==?
  • Your search runs on every keystroke. What happens with two thousand tasks?
  • If I typed <b>hello</b> as a task, what appears on screen, and why?
  • Walk me through what happens, in order, between my click and the row disappearing.
How to Prepare for Questions You Have Not Seen

The evening before, open your own file and read it top to bottom out loud, saying what each block does. Anywhere you hesitate is a question you cannot answer yet, so go and understand that part. It takes twenty minutes, and it is the highest-return thing you can do the night before any technical interview for the rest of your career.

The Marking Rubric

BandWhat it looks like
Distinction, 85+All ten requirements work. Data lives in an array and the page is rendered from it. One delegated listener. User text handled with textContent. Readable names, small functions. A README somebody else could follow. Answers every question without hesitating.
Strong, 70 to 84All ten work. Structure mostly clean with some repetition. Explains the code confidently with one or two gaps.
Pass, 50 to 69Seven or more requirements work. Code runs but reads the page as its source of truth, or attaches a listener per button. Can explain most of it.
Not yet, below 50Requirements missing, or code the student cannot explain. Resubmit Monday.

Questions You Will Have Today

I am stuck and it is 11am. Do I ask for help?

Yes, after fifteen minutes of your own attempt and not before. First write down three things: what you expected, what actually happened, and the two things you have already tried. Writing those three lines solves the problem outright surprisingly often, and when it does not, it turns "it is not working" into a question somebody can answer in thirty seconds.

Can I use code from a tutorial or from an AI?

Yes, on one condition, the same for both: you must be able to explain every line of it. You will be asked about specific lines this afternoon, picked at random.

I finished the ten requirements by 2pm. What now?

Do not add an eleventh. Delete every line that is not doing anything, rename anything vague (data, temp, x, handleClick2), and hand your laptop to the person next to you and watch them use it without helping. Every place they hesitate is a real finding.

Mine looks plain compared to the person beside me. Does that matter?

Not today. The rubric has no marks for visual design and twenty for whether the data and the render are separated properly. Make it work, then make it nice, in that order.

Tick Before You Move On

  • ☐ All ten requirements work, and I checked each one deliberately
  • ☐ My tasks live in an array and the page renders from it
  • ☐ I convert every dataset id with Number() before comparing
  • ☐ Delete uses one delegated listener and a real id, not an index
  • ☐ I tested with bold as a task name and it showed as text
  • ☐ Six or more meaningful commits and a README with a screenshot
  • ☐ I read my own code aloud and can explain every line

Quick Recap

A problem statement, not steps: the decisions are part of the assessment. One array of truth, one render function, and every action changes the array then re-renders. Derived values, such as the count of what is left, are worked out, never stored. Ids live on elements as data-id and come back as text, so convert them with Number(). Never submit code you cannot explain, whoever or whatever wrote it.

Final Scenario

It is 4pm on Friday. Your instructor clicks Delete on a task you added a minute ago, then asks you to explain exactly what happened.

You can: the click bubbled to the one listener on the list, closest found the row, Number turned its data-id into a number, the array lost that task, and render drew the page again from the array. Five sentences, every one of them yours.

That is what "make it work" really means: not only that it works, but that you know why.

Next week, Day 6: modern JavaScript and modules. From here your code starts talking to the outside world: modules, asynchronous code, HTTP and real APIs, ending in your first group build.

Learn this with a real instructor

Free lessons take you a long way on your own. The full programmes add live classes, assignments, Skill Points and a certificate — built for African students.