The Console Panel

🖥️ Lesson 3 — The Console Panel

The Console is the first place to look when something goes wrong. It collects every JavaScript error, warning, and developer log message in one place — and doubles as a live JavaScript interpreter where you can run code directly against the page. Press Ctrl + Shift + K to jump straight to it.

🗺️ The Console Interface

🚫 Errors 3 Warnings Logs Info ☐ Persist Logs
🔴 Uncaught TypeError: Cannot read properties of undefined (reading 'username') at getUserName (app.js:42:18) at init (app.js:15:5) app.js:42
🟡 Mixed Content: The page was loaded over HTTPS, but requested an insecure resource. index.html:18
Object { id: 1, name: "Alice", role: "admin" } auth.js:31
🔵 Server responded with 200 OK in 143ms api.js:88
GET https://example.com/api/users [HTTP/2 200 143ms]
>> document.querySelector('h1').textContent
💡 Console Drawer Press Escape from any other panel to open the Console as a drawer at the bottom — so you can, for example, watch the Network panel and run JavaScript at the same time without switching tabs.

🚦 Message Types at a Glance

🔴
Error
JavaScript exceptions, failed requests, security blocks. Something broke — always investigate.
🟡
Warning
Deprecated APIs, mixed content, slow operations. Non-fatal but should be addressed.
Log
Output from console.log() in the page's own JS. Lots of these on a production site means debug logging was left in.
🔵
Info
Output from console.info(). Framework messages and browser extension output often appear here.
Debug
Hidden by default. Verbose output from console.debug() — enable only when you need it.
🟣
Network
HTTP requests logged to the console (XHR/fetch). Click to jump to the Network panel entry.

🔍 Anatomy of an Error Message

Click file link to debug
Uncaught TypeError: Cannot read properties of undefined (reading 'username') ← what went wrong
at getUserName (app.js:42:18) ← where it broke (top = origin)
at init (app.js:15:5) ← what called it
at app.js:3:1 ← what called that

Read the stack trace from top to bottom — the top entry is where the exception was thrown. The entries below show the call chain that led there. Click any file:line link to jump directly to the Debugger at that line.

📚 The Console API

console.log() / .info() / .warn() / .error()
Write a message at the given level. .error() does not throw — it only styles the output red.
console.warn('Token expires in 5 min');
console.table()
Renders an array of objects as a formatted table — far clearer than a plain log() for structured data.
console.table(users); // ┌───────┬────┬───────┬───────┐ // │(index)│ id │ name │ role │ // ├───────┼────┼───────┼───────┤ // │ 0 │ 1 │ Alice │ admin │
console.group() / .groupEnd()
Nest related messages under a collapsible label. Use groupCollapsed() to start collapsed.
console.group('Auth flow'); console.log('Checking token…'); console.log('User id:', id); console.groupEnd();
console.time() / .timeEnd()
Measure how long a block of code takes. The label must match between the two calls.
console.time('data fetch'); const data = await fetchUsers(); console.timeEnd('data fetch'); // → data fetch: 143ms
console.assert()
Logs an error only if the condition is false. Silent when passing — a zero-overhead assertion.
console.assert(user !== null, 'Expected user', user); // silent if user is not null
console.count() / .countReset()
Count how many times a label has been reached — useful for tracking event firings without adding integer variables.
console.count('click'); // → 1 console.count('click'); // → 2 console.countReset('click');

🔎 Filtering Console Output

On a busy page the Console can fill with hundreds of messages. The text filter box and level toggles keep it manageable.

fetch Show only messages containing the word "fetch"
/auth|login/ Regex filter — show messages matching "auth" or "login"
-analytics Negative filter — hide all messages containing "analytics"
TypeError Show only TypeError exceptions — useful when there are multiple error types
⚠ Enable Persist Logs when debugging flows that involve page redirects. Without it, all messages from the first page are lost the moment the redirect fires. Tick the Persist Logs checkbox in the toolbar.

⚡ Running JavaScript — REPL Shortcuts

The Console input runs JavaScript in the page's own context. Everything on the page is accessible. These built-in shortcuts make common tasks faster:

ShortcutWhat it doesExample result
$('selector') Shorthand for document.querySelector() $('h1') → <h1> element
$$('selector') Shorthand for querySelectorAll() — returns a real array $$('a').length → 42
$0 The element last selected in the Inspector $0.textContent → "Hello"
$1, $2 Previously selected elements (history) $1.className → "card"
copy(value) Copy any value to the clipboard as a string copy(document.cookie)
↑ / ↓ arrow Cycle through previously run commands (history)
Shift + Enter Insert a new line without running — for multi-line snippets
ℹ️ $0 Workflow Select an element in the Inspector → switch to the Console → type $0.getBoundingClientRect(). You instantly see the element's position and dimensions without writing any code in the page source. This is one of the most practical DevTools techniques.
💡 Store as Global Variable Right-click any logged object in the Console → Store as Global Variable. Firefox assigns it to temp1, temp2, etc. You can then call methods on it, pass it to functions, or copy it without re-running the code that produced it.

✏️ Exercises

  • Audit a real site. Open the Console on any website (Ctrl + Shift + K). Note how many errors and warnings appear. Click a red error entry and read the stack trace — can you identify what went wrong and where?
  • Query the page from the REPL. Type each of these and press Enter: document.title $$('a').length $$('img').map(i => i.src)
  • Use $0. Select any element in the Inspector, switch to the Console, and run: $0.getBoundingClientRect() $0.className $0.style.border = '2px solid red'
  • Time a fetch. Run this in the Console (works on any page that allows fetch): console.time('test'); await fetch(location.href); console.timeEnd('test');
  • Read a page's performance timing as a table. Run: console.table(performance.getEntriesByType('navigation')) You'll see load timing data in a formatted table instead of a collapsed object.
  • Test filtering. Navigate to a busy site with many console messages. In the filter box, type -analytics to hide analytics noise. Then try /error|warn/ to show only errors and warnings.

📌 Key Takeaways

  • Red errors mean something broke; click the file link to jump straight to the Debugger.
  • console.table() renders arrays of objects as a table — far easier to read than plain log().
  • $0 refers to the last element selected in the Inspector — interact with it directly in the REPL.
  • $$('selector') returns a real array from querySelectorAll() — you can chain .map(), .filter() etc. on it.
  • Negative filter (-term) hides noisy messages so you can focus on errors.
  • Persist Logs keeps messages across page navigations — essential for debugging redirect flows.
  • Press Escape from any panel to open the Console drawer — watch Network and run JS simultaneously.
Up next
Lesson 4 — The Network Panel: reading every HTTP request, headers, response bodies, status codes, and the timing waterfall