About this course
Basic tier: the event loop, DOM performance and delegation. Advanced tier: async architecture, storage and web security - eight modules culminating in the PulseBoard capstone.
This is a 6-week short course - enough depth to build real projects and a portfolio piece, without a long commitment. It runs online in the August 2026 cohort (starting 1 August 2026) and is taught the EchoLens way: you learn by doing real, gradeable work rather than just watching lectures.
What's included
- Live, instructor-led online sessions across 8 weeks (20 hours total).
- Hands-on coding quests you solve inside the EchoLens browser compiler - nothing to install.
- Gems, stages and a leaderboard that keep you moving instead of grade anxiety.
- A verified certificate with a scannable QR code, ready to share on LinkedIn, when you finish.
- Completely free - no fee, just create an account and start.
What you will learn
Course outline - level by level
8 leveles, each with hands-on quests you clear in the portal.
- Level 1. Basic 1: The Event Loop, Tasks and Microtasks - JavaScript runs on a single thread, and everything that appears concurrent is actually queued. The engine finishes the current synchronous work, then drains the entire microtask queue, then takes one task from the macrotask queue and repeats. This explains why a resolved promise callback always runs before a zero millisecond timer, and why a long synchronous loop freezes the interface completely. Key rules: - Order: current synchronous code, then all microtasks, then one macrotask, then repeat. - Promise callbacks are microtasks; timer callbacks and interface events are macrotasks. - A timer set to zero milliseconds is a request, not a promise - it runs after the current work and all microtasks. - Long synchronous work blocks rendering; break it into chunks that yield between them. Worked example - execution order made explicit: console.log("1 sync"); setTimeout(() => console.log("4 macrotask"), 0); Promise.resolve().then(() => console.log("3 microtask")); console.log("2 sync"); // prints 1, 2, 3, 4
- Level 2. Basic 2: Document Internals and Render Cost - Reading a layout property forces the browser to finish any pending layout work before it can answer, so alternating reads and writes inside a loop makes the browser recompute layout on every iteration - layout thrashing, the most common cause of a page that feels slow despite fast code. The fix is to batch: read everything, then write everything, and build detached subtrees in a fragment before attaching them once. Key rules: - Reading a geometry property forces layout - alternating reads and writes in a loop forces it repeatedly. - Batch all reads, then all writes. Never interleave them inside a loop. - Build many nodes in a document fragment and attach once. - Changes to transform and opacity can be composited without a full layout pass - prefer them for animation. Worked example - batched construction with a single insertion: function renderRows(container, rows) { const frag = document.createDocumentFragment(); for (const row of rows) { const el = document.createElement("tr"); el.innerHTML = `<td>${row.name}</td><td>${row.total}</td>`; frag.appendChild(el); } container.replaceChildren(frag); // one layout pass }
- Level 3. Basic 3: Events, Propagation and Form Control - An event travels down from the document to the target, then back up. Handlers attached in the default mode fire on the way up, which is why a click on a child also triggers a parent handler. The distinction between the element that was clicked and the element the handler is attached to is what makes delegation possible. Forms add their own default behaviours that must be prevented deliberately. Key rules: - Phases: capture downward, target, then bubble upward. Handlers bubble by default. - The target property is what was interacted with; the current target is what the handler is attached to. - Preventing the default action stops the browser behaviour; stopping propagation stops other handlers - they are different. - Validate on submit, not on every keystroke. Worked example - target against current target in one handler: form.addEventListener("submit", (e) => { e.preventDefault(); const data = Object.fromEntries(new FormData(form)); if (!data.email) return show("Email is required"); submit(data); });
- Level 4. Basic 4: Event Delegation and Reactive Rendering - Attaching a listener to every element does not scale: elements added later have no listener, removed elements leak theirs. Delegation attaches one listener to a stable container and identifies the action from the event target and a data attribute. Paired with a single state object as the only source of truth, and a render function that draws the interface from that state, this produces a small reactive architecture with no framework at all. Key rules: - One listener on a stable ancestor - identify the action from a data attribute on the target. - Elements added after page load work automatically under delegation. - Keep one state object as the single source of truth - the interface is a function of that state. - Never read application state back out of the document. Worked example - one listener, one state object, one render: const state = { items: [] }; list.addEventListener("click", (e) => { const btn = e.target.closest("[data-action]"); if (!btn) return; const id = Number(btn.dataset.id); if (btn.dataset.action === "delete") state.items = state.items.filter(i => i.id !== id); render(state); });
- Level 5. Advanced 1: Promises, Concurrency and Failure Policy - Awaiting requests one after another when they do not depend on each other turns a fast page into a slow one. All rejects as soon as one fails, which is right when every result is required; all-settled reports every outcome, right when a dashboard should render whatever succeeded. Every request also needs a timeout and a retry policy. Key rules: - Awaiting sequentially adds the durations; running together takes the longest single duration. - All rejects on first failure; all-settled always resolves with the outcome of each. - Exponential backoff waits base multiplied by two to the power of the attempt number, with a cap and jitter. - Attach an abort signal to every request so slow work can be cancelled. Worked example - concurrent fetch with timeout and capped backoff: async function fetchWithRetry(url, attempts = 3) { for (let i = 0; i < attempts; i++) { const ac = new AbortController(); const timer = setTimeout(() => ac.abort(), 5000); try { const res = await fetch(url, { signal: ac.signal }); if (res.ok) return res.json(); } catch {} finally { clearTimeout(timer); } await new Promise(r => setTimeout(r, Math.min(2 ** i * 250, 4000))); } throw new Error(`failed after ${attempts} attempts`); }
- Level 6. Advanced 2: Browser Storage and Cache Strategy - The browser offers several storage mechanisms and they are not interchangeable. Simple key value storage is synchronous, string only and small; session storage clears with the tab; the indexed database is asynchronous, structured and large. The harder problem is not storage but invalidation: cached data without an expiry rule becomes wrong data that the user trusts. Key rules: - Simple key value storage is synchronous and blocks the thread - keep it small and infrequent. - Session storage clears when the tab closes; local storage persists until cleared. - Store a timestamp with every cache entry and discard anything older than its allowed age. - Storage can fail when the quota is exceeded - wrap writes and degrade gracefully. Worked example - a cache entry that knows its own expiry: const cache = { set(key, value, ttlMs) { try { localStorage.setItem(key, JSON.stringify({ value, expires: Date.now() + ttlMs })); } catch {} }, get(key) { const raw = localStorage.getItem(key); if (!raw) return null; const { value, expires } = JSON.parse(raw); if (Date.now() > expires) { localStorage.removeItem(key); return null; } return value; } };
- Level 7. Advanced 3: Web Security and Module Architecture - Cross site scripting happens when data supplied by a user is treated as markup. The defence is a boundary: user data is inserted as text, never as markup, and if markup genuinely must be rendered it passes through a sanitiser with an allow list. The cross origin policy is the browser refusing to let one origin read another's responses without permission. Key rules: - Insert user data as text content, never as markup. - Sanitise with an allow list of permitted elements and attributes - deny lists are always incomplete. - Cross origin restrictions are enforced by the browser; the response headers grant the permission. - One module, one responsibility, one export surface - circular imports are a design smell. Worked example - text insertion against markup insertion: // unsafe: user content becomes markup el.innerHTML = `<p>${comment}</p>`; // safe: user content stays text const p = document.createElement("p"); p.textContent = comment; el.replaceChildren(p);
- Level 8. Advanced 4: Application Architecture and the Course Capstone - A framework free application still needs an architecture: a state module that owns data, a data access module that owns network and storage, a render module that turns state into markup, and a controller that binds events to state changes. Every one of those can be tested alone. Accessibility belongs here too - keyboard access and focus management are architectural decisions, not a stylesheet pass. Key rules: - Four layers: state, data access, render, controller - each depends only on the one below it. - Render is a pure function of state. - Every interactive element must be reachable and operable by keyboard alone. - Set a performance budget before building and measure against it. Worked example - a render function that is pure with respect to state: function render(state) { root.replaceChildren(header(state), listView(state), footer(state)); } function dispatch(action) { state = reduce(state, action); render(state); }
How you submit: Coding quests solved in the built-in EchoLens compiler.
Who it's for
Advanced JavaScript Programming suits learners at a beginner to intermediate level who want a practical, project-based route into Advanced JavaScript Programming. You need only a browser and an internet connection - all coding runs inside the EchoLens compiler, so there is nothing to set up.
Certificate
Finish every stage and EchoLens issues a verified certificate carrying a QR code anyone can scan to confirm it on our site. You can add it to your CV or share it to LinkedIn in one click.