Table of Contents
- Module 1: Course Overview
- Module 2: Understanding the Capabilities of Chrome DevTools
- Module 3: Examining and Editing Web Pages
- Module 4: Using Device Mode to Simulate Multiple Devices
- Module 5: Using the Console
- Module 6: Debugging JavaScript
- Module 7: Viewing Network Communication and Local Data
- Module 8: Optimizing Website Performance
DevTools at a Glance
flowchart TD
DT["Chrome DevTools\n(F12 / Ctrl+Shift+C)"] --> E["Elements Panel\nDOM + Styles"]
DT --> C["Console Panel\nLogs + REPL"]
DT --> S["Sources Panel\nDebugger + Files"]
DT --> N["Network Panel\nHTTP Traffic"]
DT --> P["Performance Panel\nProfiling"]
DT --> A["Application Panel\nStorage"]
DT --> L["Lighthouse Panel\nAudit + Scores"]
DT --> DM["Device Mode\nViewport Simulation"]
E --> EP1["Styles Pane"]
E --> EP2["Computed Pane"]
E --> EP3["Event Listeners Pane"]
S --> SP1["Page Pane"]
S --> SP2["Filesystem Pane\n(Workspaces)"]
S --> SP3["Snippets Pane"]
A --> AP1["Local Storage"]
A --> AP2["Session Storage"]
A --> AP3["Cookies"]
A --> AP4["Cache Storage"]
A --> AP5["IndexedDB"]
Module 1: Course Overview
Google Chrome ships with a powerful set of tools to help developers debug websites. There is no more appropriate place to debug client-side web apps than right in the application where they run. Chrome DevTools provides sophisticated functionality to help find and fix bugs faster.
Major topics covered:
- Examining and editing the DOM
- Using the browser console
- Debugging JavaScript
- Viewing network communication
- Optimizing website performance
Prerequisites: A working familiarity with the basics of HTML, CSS, and JavaScript.
Module 2: Understanding the Capabilities of Chrome DevTools
The Importance of Debugging in the Browser
Chrome increments its major version number approximately once a month, though each release typically includes only a handful of relatively minor changes. The core DevTools interface remains stable across many versions.
If you have spent any time writing code, you have probably seen your program do something you did not expect and then spent more time than you care to admit trying to figure out how to fix it. You can and should work at being a better developer and creating fewer bugs. However, fewer bugs will never be zero bugs. While you are working on getting better, you also need to embrace the failure, know that bugs will always be part of the process, and learn how to fix them faster. That is the goal of this course.
The DevTools built into Chrome are surprisingly powerful and let you debug problems right in the environment where they exist. There is less of the frustrating “well, it works on my machine” situation when you can literally debug your code inside the same application where your users are running it.
The three-step debugging process:
flowchart LR
A["🐛 Bug Observed"] --> B["1. Find It\nLocate where in\nthe code it occurs"]
B --> C["2. Identify It\nUnderstand exactly\nwhat is wrong"]
C --> D["3. Fix It\nCorrect the code"]
D --> E["✅ Verify\nTest the solution"]
style A fill:#f66,color:#fff
style E fill:#6a6,color:#fff
Almost everything DevTools offers falls into one of those three categories: find it, identify it, fix it.
Chrome DevTools is naturally focused on browser technologies — HTML, CSS, and JavaScript. Investing time in learning DevTools will be repaid many times over in the time saved fixing problems.
Demo Project Overview and Debugging Scenarios
The demo application used throughout this course is Bethany’s Pie Shop — a small fictional bakery website. It is a nice-looking informational site with all the elements needed to develop client-side debugging skills.
Key features of the demo project:
- Served over HTTP using a small Node.js + Express web server running on
localhost:3000. Serving over HTTP allows observing and troubleshooting communication between browser and server. - Several static HTML pages: Home, About, All Pies, Apple Pie details, Fruit Pies, Cheesecakes, Seasonal Pies.
- A dynamic All Pies page that uses JavaScript and the Fetch API to retrieve a list of pies from the server and display them on the page.
Running the demo server:
node server.js
Demo Express server (server.js):
const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const bodyParser = require('body-parser');
const pies = require('./server/routes/pies');
const app = express();
app.use(favicon(path.join(__dirname, 'dist/favicon.png')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'dist')));
app.use('/api/pies', pies);
app.set('port', process.env.PORT || 3000);
app.listen(app.get('port'));
console.log('Listening on port: ' + app.get('port'));
module.exports = app;
Pie data API route (server/routes/pies.js):
const express = require('express');
const fs = require('fs');
const datafile = 'server/data/pies.json';
const router = express.Router();
router.route('/')
.get(function(req, res) {
let rawData = fs.readFileSync(datafile, 'utf8');
let pieData = JSON.parse(rawData);
res.send(pieData);
});
module.exports = router;
Sample pie data (server/data/pies.json):
[
{ "pieID": 1, "name": "Peach pie", "description": "Chilton County peaches!", "imageURL": "images/products/peachpiesmall.jpg", "price": 18.95 },
{ "pieID": 2, "name": "Apple pie", "description": "John Appleseed loves it!", "imageURL": "images/products/applepiesmall.jpg", "price": 14.95 },
{ "pieID": 3, "name": "Cranberry pie", "description": "Lots of sugar in this one.", "imageURL": "images/products/cranberrypiesmall.jpg", "price": 18.95 },
{ "pieID": 4, "name": "Cherry pie", "description": "Made with canned cherries. Yum!", "imageURL": "images/products/cherrypiesmall.jpg", "price": 18.95 },
{ "pieID": 5, "name": "Cheese cake", "description": "Plain cheese cake. Meh.", "imageURL": "images/products/cheesecakesmall.jpg", "price": 12.95 },
{ "pieID": 6, "name": "Pumpkin pie", "description": "Simple and delicious.", "imageURL": "images/products/pumpkinpiesmall.jpg", "price": 18.95 },
{ "pieID": 7, "name": "Christmas apple pie", "description": "A pricey holiday treat.", "imageURL": "images/products/christmasapplepiesmall.jpg", "price": 29.95 },
{ "pieID": 8, "name": "Blueberry cheese cake", "description": "Better than the plain one!", "imageURL": "images/products/blueberrycheesecakesmall.jpg", "price": 17.95 },
{ "pieID": 9, "name": "Rhubarb Pie", "description": "Tastes like chicken.", "imageURL": "images/products/rhubarbpiesmall.jpg", "price": 17.95 },
{ "pieID": 10,"name": "Strawberry Pie", "description": "Lots of berries. No straws.", "imageURL": "images/products/strawberrypiesmall.jpg", "price": 18.95 },
{ "pieID": 11,"name": "Strawberry Cheese Cake", "description": "Also better than plain!", "imageURL": "images/products/strawberrycheesecakesmall.jpg", "price": 21.95 }
]
Node.js is required to run the server. Install the LTS version from nodejs.org.
Exploring What’s Possible in Chrome DevTools
There are multiple ways to open DevTools. The most important and quickest is pressing F12 (also works on Linux). On macOS, use Command+Option+I.
Pressing F12 splits the browser window: the site at the top, DevTools at the bottom. DevTools is almost like having a full-blown integrated development environment (IDE) right in your browser — it contains features similar to those found in large tools designed specifically for software development.
Panels and Panes:
Most functionality is organized into panels across the top of the DevTools window. Each panel loads a unique interface below. Within some panels (notably Elements), there are secondary panes on the right side.
| Panel | Purpose |
|---|---|
| Elements | Examine DOM elements and their CSS styles |
| Console | Log messages, JavaScript REPL |
| Sources | View files, set breakpoints, step through code |
| Network | Monitor HTTP requests and responses |
| Performance | Profile runtime behavior and performance |
| Application | Local storage, cookies, cache, IndexedDB |
| Lighthouse | Automated site quality audits |
Configuring DevTools:
- Access Settings via the gear icon (⚙️) at the top right.
- Settings include theme selection (dark/light), panel layout preferences, and many panel-specific options.
- Use the dock position menu (three-dot menu next to the gear icon) to place DevTools at the bottom, left, right, or in its own window. Undocking into its own window provides extra screen real estate when analyzing large amounts of information.
- Close DevTools with F12 again or the X in the corner.
The Command Menu:
The DevTools Command Menu works similarly to the Command Palette in Visual Studio Code. Open it with Ctrl+Shift+P (Windows/Linux) or Command+Shift+P (macOS). Start typing any text related to the command you want, and the Command Menu will locate it. For example, typing “dark” finds the option to switch to the dark theme immediately.
Official Documentation: https://developer.chrome.com/docs/devtools/
Module 3: Examining and Editing Web Pages
Understanding the Difference between HTML and the DOM
A page’s HTML is not the same as the Document Object Model (DOM) represented in the browser. Once a page finishes loading, the HTML initially loaded might be an exact textual representation of the DOM — but this changes quickly in modern sites that make heavy use of JavaScript.
Example — HTML vs. DOM divergence:
Consider a small static About page. Opening it in the Sources panel shows the original HTML file exactly as it was downloaded from the server. However, if you run JavaScript in the console to append a new element to the DOM:
// Add a new element to the DOM via the console
document.body.appendChild(document.createElement('h3'));
document.querySelector('h3').innerText = "I'm NOT in the HTML source file";
The new <h3> element becomes visible on the page. But if you switch back to the Sources panel and inspect the original about.html, it looks exactly as it did before. The JavaScript modified the DOM, not the HTML source.
Now switch to the Elements panel. Rather than showing the HTML source, it reflects the actual DOM as it currently exists — including that new <h3> element.
Key takeaway:
flowchart LR
HTML["HTML Source File\n(Sources Panel)"]
DOM["Live DOM\n(Elements Panel)"]
JS["JavaScript Execution"]
HTML -->|"Initial load"| DOM
JS -->|"Runtime modifications\n(appendChild, innerHTML...)"| DOM
DOM -.->|"May diverge from"| HTML
- The Sources panel shows the HTML as it was sent from the server.
- The Elements panel shows the current live state of the DOM.
- JavaScript can modify the DOM at any time, causing the two to diverge.
Using the Elements Panel to Find and Modify Items in the DOM
Opening the Elements panel:
| Shortcut | Platform |
|---|---|
F12 then click Elements | Windows / Linux / macOS |
Ctrl+Shift+C | Windows / Linux (opens directly to Elements) |
Command+Shift+C or Command+Option+C | macOS |
Navigating the DOM tree:
The Elements panel represents the DOM as a hierarchical tree. Clicking any element in the tree highlights the corresponding area on the web page with a blue rectangle overlay. As you navigate deeper into the tree, the highlight narrows to match the selected element. The Styles pane on the right automatically updates to show the styles applied to the currently selected element.
Quick selection techniques:
- Right-click and Inspect: Right-click on any visible element on the page and choose Inspect to jump directly to that element in the Elements panel. This works for virtually anything on the page.
- Element Selector button: Click the element selector icon (top-left of the DevTools toolbar). As you move the mouse over the page, DevTools highlights and selects elements dynamically. Click to lock the selection.
- Search (Ctrl+F / Command+F): Opens a search box at the bottom of the Elements panel. You can search by:
- Plain text string
- CSS selector (e.g.,
#mainheaderfinds an element with that ID) - XPath query
Modifying DOM elements:
Once you have found an element in the Elements panel, you can edit it directly:
- Double-click any text content to edit it inline.
- Double-click any attribute value to change it.
- Right-click an element to add, move, copy, or delete it.
- All changes are reflected immediately in the browser view.
Note: Changes made in the Elements panel are not automatically saved back to your source files unless you have configured Workspaces (see the next section).
Troubleshooting and Modifying CSS Styles
The Elements panel is particularly powerful for troubleshooting and manipulating CSS styles.
The Styles pane:
When an element is selected in the Elements panel, the Styles pane on the right shows all the CSS rules being applied to it. The cascade is displayed from bottom to top:
- Bottom: Default browser styles
- Middle: CSS rules from linked stylesheets (ordered by specificity)
- Top: Inline styles applied directly to the element
Rules that have been overridden by higher-priority styles appear with a strikethrough through them. This makes it straightforward to trace exactly where a style is being changed and what is overriding it.
Example — Debugging incorrect link colors:
Imagine navigation links that appear yellow instead of white, and turn red on hover. The investigation workflow:
- Right-click the link and choose Inspect to open the Elements panel on that element.
- In the Styles pane, scan from top to bottom for the color-related rules. A style that was expected to apply but has a strikethrough is being overridden elsewhere.
- Find the offending rule — in this case, a new
link_styles.min.cssstylesheet added anavlinksclass setting the color to yellow. - Click the link to the file name in the Styles pane — DevTools opens the file in the Sources panel, moved to the relevant location.
- The minified file can be formatted for readability by clicking the Format button (bottom of the Sources panel). The formatted code reveals the class setting
color: yellowand the hover state settingcolor: red.
Key Styles pane features:
- Computed pane: Click Computed (next to Styles) to see the final calculated value of every CSS property on the element, regardless of which stylesheet set it. This removes the cascade complexity and shows only end results.
- Toggling properties: Uncheck the checkbox next to any CSS property to disable it temporarily. The page updates immediately, allowing you to verify the impact.
- Element state simulation: The
:hovbutton in the Styles pane opens a menu with pseudo-states (hover,focus,active,visited, etc.) you can force-enable. This lets you inspect hover styles without physically hovering. - Adding/modifying rules: Click on any property value to edit it. Click in an empty area inside a rule block to add a new property. Use the
+icon that appears when hovering over a rule block to add a new rule in the same stylesheet. .clsbutton: Quickly add existing CSS classes to the selected element by clicking.clsand typing a class name.
Box model manipulation:
At the bottom of the Styles pane is a box model diagram showing the element’s margin, border, padding, and content dimensions. Colors match the overlays on the page:
- Blue: Content area
- Green: Padding Yellow: Border
- Orange: Margin
Double-click any value in the diagram to change it. Changes are immediately visible on the page, making it easy to fine-tune positioning and spacing.
Saving Changes to Files with Workspaces
Because it is so easy to find and modify client-side code in DevTools, it is natural to want to save those changes back to source files. The Workspaces feature makes this possible.
The Sources panel — Page pane vs. Filesystem pane:
The default view of the Sources panel is the Page pane, which shows all files downloaded from the web server. These are the files as sent from the server (indicated by a cloud icon). The Filesystem pane allows mapping those files to the actual files on your local disk.
Setting up a workspace:
- In the Sources panel, click the two small arrows (>>) to reveal additional panes.
- Select Filesystem.
- Click Add folder to workspace and navigate to the root folder of your project.
- When prompted, click Allow to grant DevTools full access to that location.
- The full project folder hierarchy now appears in the Filesystem pane.
Green dot indicator: Once a workspace is configured, CSS and JavaScript files that are successfully mapped to local disk files display a small green dot. This indicator appears both in the Filesystem pane and the Page pane, confirming that any changes made in DevTools will be synced to the source files on disk.
Practical workspace workflow:
With a workspace configured, you can:
- Find a style problem in the Styles pane.
- Make the fix directly in DevTools.
- The change is immediately saved back to the corresponding source file on disk.
- No need to manually switch to your editor to make the same change again.
Example workflow for fixing link colors:
- Turn on the hover state for the navigation link using the
:hovbutton. - In the Styles pane, scroll to the first rule in
styles.css. - Click the
+icon to create a new rule targeting the hover state of links inside the nav bar:nav a:hover { color: yellow; }. - The change is immediately visible and saved to the
styles.csssource file on disk. - To complete the fix, remove the offending
link_styles.min.cssreference from the HTML.
Remember: Changes are not permanent until the workspace is configured. Without a workspace, refreshing the page will discard all in-DevTools edits.
Module 4: Using Device Mode to Simulate Multiple Devices
Simulating Multiple Device Viewports
People visit sites on devices of all shapes and sizes. DevTools helps ensure your site looks and works well across different form factors with Device Mode.
Activating Device Mode:
Click the Device Mode button (looks like a phone sitting on top of a tablet) to the right of the element selector on the DevTools toolbar. A new toolbar appears above the simulated viewport.
Device Mode toolbar controls:
| Control | Purpose |
|---|---|
| Width × Height fields | Enter a specific resolution |
| DPR dropdown | Set Device Pixel Ratio |
| Device type menu | Emulate Desktop or Mobile (touch vs. non-touch) |
| Throttling dropdown | Basic CPU and network throttling |
| Rotate button | Simulate device rotation (landscape/portrait) |
Adjusting viewport size:
- Type specific pixel values in the width and height fields.
- Drag the handles on the edges of the viewport manually.
- Use the preset breakpoint bar (thin gray bar below the toolbar) to snap to common breakpoints: small mobile, medium mobile, tablet, etc.
- Use the device presets dropdown (far left of the toolbar) to select pre-configured popular devices:
- Galaxy series, Pixel phones
- iPhone and iPad (various generations)
- Custom devices
Custom device configuration:
From the device dropdown, select Edit to open Settings with all built-in devices listed. Click Add custom device to define a new device with a specific resolution, pixel ratio, user agent string, and orientation support.
Throttling Performance
Developers typically have access to very good hardware and fast networks. This can make it easy to forget that your users may be on much older, slower equipment. DevTools lets you throttle performance to test the experience those users would have.
Device Mode throttling (quick):
The throttling dropdown in the Device Mode toolbar offers three quick presets:
| Preset | CPU Throttle | Network |
|---|---|---|
| No throttling | None | Full speed |
| Mid-tier mobile | 4× slowdown | Fast 3G |
| Low-end mobile | 6× slowdown | Slow 3G |
| Offline | N/A | No network |
Performance panel throttling (granular):
For more fine-grained control, turn off Device Mode and switch to the Performance panel. Click the gear icon (⚙️) specific to the Performance panel (top right of the panel) to reveal additional settings:
- CPU throttling: Set an independent CPU slowdown multiplier (4× or 6×).
- Network throttling: Choose from presets or create custom profiles with specific upload/download speeds and latency.
These settings can be adjusted independently, unlike the combined Device Mode presets. When you are finished testing, reset both settings to No throttling.
Emulating Geolocation and Orientation Sensors
Mobile devices have sensors not typically found in traditional desktop computers. DevTools lets you emulate those sensors without buying multiple physical devices.
Opening the Sensors tab:
- Click the three-dot menu (⋮) in DevTools.
- Select More tools → Sensors.
The Sensors tab opens in a drawer at the bottom of DevTools.
Geolocation sensor:
The geolocation sensor override is useful when you need to see how your site behaves in different parts of the world or how mapping features respond to different coordinates.
- Select a pre-configured city from the dropdown (e.g., Berlin) to fill in latitude and longitude automatically.
- Choose Other to type custom coordinates.
- Click Manage → Add location to save a new preset to your personal list.
- Select No override to return to using the actual device location.
Orientation sensor:
The orientation sensor lets you specify alpha, beta, and gamma values to test how your site responds to device rotation and tilt — without physically tilting anything. You can also use the preset orientation shortcuts for common positions like portrait, landscape, etc.
Module 5: Using the Console
Understanding, Navigating, and Creating Log Messages
The browser console is a seemingly simple tool, but it includes powerful features that are very helpful when debugging. Most developers first encounter DevTools when opening the console to review log messages.
Filtering console output:
When debugging, log messages can pile up quickly. The console provides several ways to filter them:
- Log level filter buttons (top toolbar): Show or hide messages by level: All, Verbose, Info, Warnings, Errors.
- Text filter box: Type any string to show only messages containing that text.
- Source file filter: Click a file link in the console to filter to messages from that file.
- Hide network errors: A toggle to suppress network-related errors if they are not relevant to the problem.
Log level methods:
console.log('General message'); // Default, no special formatting
console.info('Informational text'); // Plain message (similar to log)
console.warn('Something seems off'); // Yellow background + warning icon
console.error('Something failed'); // Red background + error icon
Example — loadNutritionInfo function demonstrating log levels:
function loadNutritionInfo() {
console.info("Attempting to retrieve the nutritional information from the server.");
fetch("/api/nutrition")
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error("There was a problem retrieving the nutritional information."));
console.warn("Eating too much pie is not good for you!");
}
When loadNutritionInfo is called:
- The
infomessage appears first. - The
warnmessage appears with a yellow background and warning icon. - The browser generates its own network error for the failed
/api/nutritionrequest (the endpoint does not exist). - The custom
errormessage appears below the browser’s error.
Note on ordering: Messages may not appear in exactly the same order as they appear in the code. Asynchronous calls (like
fetch) are processed after the synchronous code completes.
Debugging with the Console API
The Console API includes many methods beyond the basic log, info, warn, and error methods.
console.dir(object)
Outputs a hierarchical, interactive JSON representation of an object. Useful for exploring objects with nested properties.
fetch("/api/pies")
.then(response => response.json())
.then(data => {
console.dir(data); // Hierarchical JSON view, expandable
console.table(data); // Tabular view with column headers
});
console.table(data)
Displays array data as a formatted table with one row per item, column headers for each property, and an index column. Excellent for small sets of objects with a few properties.
console.groupCollapsed(label) / console.groupEnd()
Groups related log messages into a single collapsible node labeled with the provided string. This helps keep the console organized and easier to navigate when debugging.
console.groupCollapsed('pie data');
console.dir(data);
console.table(data);
console.groupEnd();
console.assert(condition, message)
Similar to unit test assertions. The first parameter should evaluate to a boolean — if it is true, nothing is output. If it is false, the message (second parameter, an object) is logged as an error.
// Assert that 11 pies were returned
console.assert(data.length === 11, {
pieCount: data.length,
reason: 'Wrong number of pies'
});
If the assertion fails, you get a clear error message with context. When you fix the code and the condition becomes true, no output is produced.
console.trace()
Logs the full call stack at the point where it is called — with links to the source file and line number for each frame. Useful when a function is being called unexpectedly and you need to discover where the call originates.
function loadAllPies() {
fetch("/api/pies")
.then(response => response.json())
.then(data => {
// ... display logic ...
console.trace(); // Shows the call stack
});
}
Running JavaScript and Using the Utilities API
The most powerful feature of the console is its ability to write and run arbitrary JavaScript code. The console is a JavaScript REPL (Read–Evaluate–Print Loop): it reads a line of code, evaluates it, prints any result, and waits for the next input.
Basic REPL usage:
5 + 3 // → 8
console.log('Hello, World!') // outputs "Hello, World!", returns undefined
function sum(a, b) {
return a + b;
}
sum(10, 5) // → 15
The return value of each expression appears next to a left-pointing arrow (←). Functions return undefined when defined; they return their return value when called.
Accessing page elements from the console:
Code written in the console has full access to the page’s JavaScript environment:
// Access elements just like your app code
document.querySelector('h1').innerText = 'Modified from console';
document.querySelectorAll('nav a').length; // Count nav links
The Console Utilities API:
The Console Utilities API is a set of convenience functions available only in the DevTools console (not in regular code).
| Utility | Description |
|---|---|
$0 | The element currently selected in the Elements panel |
$1, $2, $3, $4 | Previously selected elements (in reverse order) |
$(selector) | Shortcut for document.querySelector(selector) |
$$(selector) | Shortcut for document.querySelectorAll(selector), returns an array |
getEventListeners(element) | Returns all event listeners registered on an element |
monitor(function) | Logs a message each time the function is called |
inspect(element) | Opens the element in the Elements panel |
Example — using $0 and $1:
- Use the element selector to select the All Pies link on the page. It is now
$0. - Select a different element (e.g., the pie logo image). That becomes the new
$0. - The All Pies link is now available as
$1.
$0 // → the pie logo image (currently selected)
$1 // → the All Pies link (previously selected)
Example — writing a helper function in the console:
// Define a function to log events
function logEvent(event) {
console.log('Event occurred:', event.type);
}
// Use it with $0 (currently selected element)
$0.addEventListener('click', logEvent);
$0.addEventListener('mouseover', logEvent);
Creating and Monitoring Live Expressions
Live Expressions let you define a JavaScript expression that is evaluated in real time and pinned to the top of the console. They are useful for observing values that change as you interact with the page.
Creating a live expression:
- Click the eye icon (👁) to the left of the filter box in the console toolbar.
- Type the JavaScript expression you want to monitor.
- Press Ctrl+Enter (Windows/Linux) or Command+Enter (macOS) to confirm.
- The expression is pinned to the top of the console and continuously evaluated.
Example 1 — monitoring the current page URL:
document.location.href
As you navigate between pages, the expression updates in real time.
Example 2 — monitoring event listeners on an element:
// After declaring: let logo = $0 (the pie image)
getEventListeners(logo)
As you add event listeners to the element, the live expression updates to show all registered listeners with their respective arrays.
// Add listeners to see the live expression change
logo.addEventListener('click', () => console.log('click'));
logo.addEventListener('mouseenter', () => console.log('mouseenter'));
// Live expression now shows: { click: [...], mouseenter: [...] }
Key behaviors:
- Live expressions are preserved even after clearing the console.
- Multiple live expressions can be pinned simultaneously.
- They can reference any JavaScript accessible in the page context.
- They are ideal for values that change unexpectedly — you can see exactly when and how they change.
Module 6: Debugging JavaScript
Understanding and Using the Sources Panel
The Sources panel is the primary place to debug JavaScript. It has three main sections:
- Left sidebar: File navigator (Page, Filesystem, Snippets panes)
- Center: Code editor with syntax highlighting
- Right sidebar: Debugging controls (call stack, scope, breakpoints, etc.)
flowchart TD
SP["Sources Panel"] --> Left["Left Sidebar"]
SP --> Center["Code Editor"]
SP --> Right["Right Sidebar"]
Left --> LP["Page Pane\n(Server files)"]
Left --> FS["Filesystem Pane\n(Workspace files)"]
Left --> SN["Snippets Pane"]
Right --> CS["Call Stack"]
Right --> SC["Scope / Variables"]
Right --> BP["Breakpoints List"]
Right --> ELB["Event Listener Breakpoints"]
Right --> WB["Watch Expressions"]
Demo code — setSeasonalLink and isFreshPieSeason:
The demo app uses the following JavaScript to determine which seasonal pies message to display (a bug exists that we will fix):
window.onload = function() {
loadAllPies();
setSeasonalLink();
setFavorite();
};
function setSeasonalLink() {
let seasonalElement = document.getElementById("seasonal");
let fresh = isFreshPieSeason();
if (fresh) {
seasonalElement.innerText = "Fresh Seasonal Pies";
} else {
seasonalElement.innerText = "Frozen Pies";
}
}
// BUG: JavaScript months are zero-based (0 = January, 11 = December)
// This function incorrectly identifies "fresh pie months"
function isFreshPieSeason() {
let currentMonth = new Date().getMonth();
if (currentMonth >= 9 || currentMonth <= 1) {
return true; // October–February
} else {
return false; // March–September
}
}
Demo code — setFavorite with local storage (main.js):
function saveFavorite(favorite) {
localStorage.setItem('favoritePie', favorite);
}
function getFavoritePie() {
let favorite = 'No favorite specified.';
if (window.localStorage) {
let storage = window.localStorage;
if (storage.getItem('favoritePie')) {
favorite = storage.getItem('favoritePie');
}
}
return favorite;
}
Log points — debug without modifying source code:
A log point is a special type of breakpoint that logs a message to the console without pausing execution. This is a cleaner alternative to adding console.log statements to your code.
To add a log point:
- Right-click in the left margin beside the target line.
- Select Add log point.
- Type the message in the box (you can include variable references in
{ }— e.g.,Current favorite is: {newFavorite}). - Press Enter.
The log point is shown with a distinct icon (different color from regular breakpoints). When the code runs through that line, the message is logged to the console — and you do not have to remove anything from your source files when done.
Creating Breakpoints and Stepping Through Code
Types of breakpoints in DevTools:
flowchart TD
BPT["Breakpoint Types"] --> LB["Line Breakpoint\nClick in left margin\nPauses execution at that line"]
BPT --> CB["Conditional Breakpoint\nRight-click → Edit breakpoint\nPauses only when expression is true"]
BPT --> LP2["Log Point\nRight-click → Add logpoint\nLogs message, does not pause"]
BPT --> ELB2["Event Listener Breakpoint\nRight sidebar → Event Listener Breakpoints\nPauses on any matching event"]
BPT --> DOM["DOM Breakpoint\nRight-click element in DOM\nPauses on subtree/attribute changes"]
Setting a regular line breakpoint:
Click in the left margin (the line number gutter) beside any executable line of code. A blue marker appears to indicate the breakpoint. Click it again to remove it.
Setting a conditional breakpoint:
- Add a normal breakpoint by clicking the margin.
- Right-click the breakpoint marker and select Edit breakpoint.
- A popup appears. Type a JavaScript expression — the breakpoint only triggers when the expression evaluates to
true.
// Example condition: only pause when the pie name contains "cake"
newFavorite.includes('cake')
With this condition, clicking “Peach pie” or “Apple pie” runs without pausing. Clicking “Cheese cake” pauses execution because the condition is true.
Event listener breakpoints:
In the right sidebar, expand Event Listener Breakpoints. The list is organized by category (Mouse, Keyboard, Touch, etc.). Checking an event type (e.g., click) causes DevTools to pause execution whenever that type of event is handled anywhere on the page — without requiring you to find and modify the specific handler in code.
DOM breakpoints:
- Select an element in the Elements panel.
- Right-click it and choose Break on → Subtree modifications (or Attribute modifications, Node removal).
- Now any JavaScript that modifies that element or its children will pause execution at the line causing the change.
Stepping through code — the debugger toolbar:
Once paused at a breakpoint, a toolbar with stepping controls appears:
| Button | Action | Shortcut |
|---|---|---|
| ▶ Continue | Resume until the next breakpoint | F8 |
| ⤵ Step over | Execute the current line, move to the next | F10 |
| ⬇ Step into | Enter the function call on the current line | F11 |
| ⬆ Step out | Finish the current function, return to caller | Shift+F11 |
| → Step | Move to the next line of code | F9 |
| ⊘ Deactivate breakpoints | Disable all breakpoints temporarily | — |
While paused, the right sidebar shows:
- Scope: All variables in scope at the current execution point with their current values.
- Call Stack: The chain of function calls that led to the current pause point.
- Breakpoints: A list of all currently set breakpoints.
Debugging the isFreshPieSeason bug:
Using the Sources panel to find and fix the zero-based month index bug:
- Add a breakpoint in
setSeasonalLinkor set a DOM breakpoint on the seasonal link element. - Refresh the page to trigger the code.
- Step through to
isFreshPieSeason. Examine the Scope section —currentMonthis2when it should be3for March. - Verify in the console:
new Date().getMonth()returns2(JavaScript months are zero-based: January = 0, December = 11). - Fix the condition directly in the code editor while the debugger is paused:
// Fixed: October (9) through February (1)
// Previously: currentMonth >= 9 || currentMonth <= 1
// That was already correct — but the confusion arose from assuming March = 3 when in fact getMonth() returns 2
// The bug is understanding that getMonth() is 0-indexed
- Click Continue — the link now correctly shows “Frozen Pies” when recorded in March.
Using Source Maps to Debug Preprocessed Code
Most modern web applications include a build step that:
- Combines multiple JavaScript/CSS files into one.
- Minifies the output to reduce file size.
Minified code is nearly impossible to debug directly. Source maps solve this problem.
What is a source map?
A source map is a file (typically filename.js.map) that maps positions in the minified/compiled output back to the original source files. DevTools uses it to present the original, readable code in the debugger even though the browser executes the minified version.
Minified pieshop.js (difficult to read):
window.onload=function(){loadAllPies();setSeasonalLink();setFavorite()};function setSeasonalLink(){let seasonalElement=document.getElementById("seasonal");let fresh=isFreshPieSeason();if(fresh){seasonalElement.innerText="Fresh Seasonal Pies"}else{seasonalElement.innerText="Frozen Pies"}}function isFreshPieSeason(){let currentMonth=new Date().getMonth();if(currentMonth>=9||currentMonth<=1){return true}else{return false}}...
How DevTools handles source maps:
When the minified file contains a //# sourceMappingURL=pieshop.js.map comment (automatically added by bundlers like Babel, webpack, etc.), DevTools:
- Loads the source map file.
- Shows the original source files (
allPies.js,main.js) in the Sources panel in italics (indicating they are loaded from a source map, not directly referenced on the page). - Hovering over an italicized file name shows a tooltip: “loaded from source map.”
- Debugging proceeds using the original source files — breakpoints, step-through, variable inspection — even though the browser runs the minified code.
Enabling source maps:
Source map support is enabled by default but can be verified under: Settings → Sources → Enable JavaScript source maps ✓
Build configuration (Babel example):
The demo project uses Babel to combine allPies.js and main.js into the minified pieshop.js with an accompanying pieshop.js.map:
dist/js/pieshop.js ← minified bundle
dist/js/pieshop.js.map ← source map
Creating and Using Snippets
Snippets are small pieces of JavaScript saved in the Sources panel that can be quickly executed to assist with debugging. They are perfect for diagnostic code you use repeatedly across multiple debugging sessions without adding it to your actual source code.
Accessing Snippets:
- In the Sources panel, click the
>>arrows to reveal additional panes. - Select Snippets from the dropdown.
- A list of your saved snippets appears, along with a New snippet button.
Creating and running a snippet:
- Click New snippet and give it a descriptive name (e.g., “Basic Page Info”).
- Type your JavaScript in the editor:
// Snippet: Basic Page Info
const info = {
URL: window.location.href,
title: document.title
};
console.table(info);
-
Run the snippet:
- Click the play button (▶) at the bottom of the editor, or
- Press Ctrl+Enter.
-
The console drawer opens and displays the output.
When to use snippets:
- Code that helps inspect the current state of a page that you use across many different debugging sessions.
- Utility functions that simplify repetitive console operations.
- Any diagnostic code you do not want to commit to your actual source files.
Tip: Unlike console history, snippets are persistent across browser sessions. They are stored by DevTools and available whenever you open it.
Module 7: Viewing Network Communication and Local Data
Using the Network Panel to Inspect Server Communication
Most web application bugs are in either client-side or server-side code — but the HTTP traffic flowing between them often contains diagnostic information that can help you pinpoint the source of the problem faster.
sequenceDiagram
participant Browser
participant Server
Browser->>Server: HTTP Request\n(GET /api/pies)
Server-->>Browser: HTTP Response\n(200 OK + JSON data)
Note over Browser,Server: DevTools Network Panel\ncaptures all traffic in both directions
Browser->>Server: HTTP Request\n(GET /index.html)
Server-->>Browser: HTTP Response\n(200 OK + HTML)
Important: The Network panel only records traffic while DevTools is open. If you navigate to a page before opening DevTools, you will not see those requests. Either open DevTools first, or refresh the page after opening.
Network panel layout:
- Toolbar: Filter buttons, disable cache checkbox, throttling, clear, settings.
- Requests grid: One row per HTTP request. Default columns: Name, Status, Type, Initiator, Size, Time. Additional columns can be added by right-clicking any column header.
- Details pane: Clicking a request reveals its full details.
Customizing columns:
Right-click any column header to see available columns and add/remove them. For example, adding the Method column shows whether each request was a GET, POST, etc.
Filter buttons:
| Filter | Shows |
|---|---|
| All | Everything |
| Fetch/XHR | Background JavaScript requests (Fetch API, XMLHttpRequest) |
| JS | JavaScript file downloads |
| CSS | Stylesheet downloads |
| Img | Image downloads |
| Media, Font, Doc, WS, Wasm, Manifest, Other | Other resource types |
Inspecting a specific request:
Click any row to open the details pane with multiple sub-tabs:
| Sub-tab | Content |
|---|---|
| Headers | Request headers, response headers, status code, URL |
| Preview | Formatted preview of the response body (e.g., rendered JSON tree) |
| Response | Raw response body text |
| Initiator | What triggered the request (code location, call stack) |
| Timing | Detailed breakdown of time spent at each phase (DNS, TCP, TTFB, download) |
| Cookies | Cookies sent with the request and received in the response |
Disable cache:
The Disable cache checkbox on the toolbar forces Chrome to always make real network requests, ignoring any cached resources. This should almost always be enabled when debugging to ensure you see every request the page actually makes.
Simulating slow networks from the Network panel:
Use the throttling dropdown on the toolbar to select network speed presets or custom profiles (see Module 4).
Viewing and Editing Local Storage
DevTools provides two ways to view and manipulate local storage: the Application panel and the console.
Application panel — Local Storage:
- Open the Application panel (far right of the panel list).
- In the left sidebar, expand Local Storage.
- Click on the origin (e.g.,
localhost:3000). - The main area shows a key-value grid (top) and a preview of the selected value (bottom).
Manipulating local storage in the Application panel:
| Operation | How |
|---|---|
| Add a new key | Double-click in the key column area below existing keys |
| Edit a key name | Double-click the key name |
| Edit a value | Double-click the value cell |
| Delete a key | Select the row, press Delete or right-click → Delete |
| Clear all | Use the clear button (🚫) above the grid |
Important: Changing values in the Application panel only updates the stored data. It does not automatically re-run the JavaScript code that reads those values. Refresh the page (or re-run the relevant code) to see the effect.
main.js — Local Storage JavaScript API:
function saveFavorite(favorite) {
localStorage.setItem('favoritePie', favorite);
}
function getFavoritePie() {
let favorite = 'No favorite specified.';
if (window.localStorage) {
let storage = window.localStorage;
if (storage.getItem('favoritePie')) {
favorite = storage.getItem('favoritePie');
}
}
return favorite;
}
Application panel — Local Storage from the console:
The browser exposes a localStorage JavaScript API in the console:
// Get a key by index
localStorage.key(0); // → "favoritePie"
// Get a value by key name
localStorage.getItem('favoritePie'); // → "Apple pie"
// Set a value
localStorage.setItem('favoritePie', 'Peach pie');
// Remove a specific key
localStorage.removeItem('favoritePie');
// Clear all local storage for this origin
localStorage.clear();
After calling localStorage.clear(), switching back to the Application panel confirms that all data is gone.
Inspecting and Deleting Cookies
The Application panel also provides a simple interface for managing cookies.
Viewing cookies:
- In the Application panel left sidebar, expand Cookies.
- Click on the URL for your site.
- A grid lists all cookies with columns: Name, Value, Domain, Path, Expires/Max-Age, Size, HttpOnly, Secure, SameSite.
Operations on cookies:
| Operation | How |
|---|---|
| Add a cookie | Double-click below the last row in the Name column |
| Edit a cookie | Double-click the cell to change Name or Value |
| Sort cookies | Click a column header |
| Filter cookies | Type in the filter box above the grid |
| Delete a cookie | Select row + click Delete Selected button |
| Delete all cookies | Click the Clear all button (to the left of Delete Selected) |
Example cookies in the demo app:
most_recent_pie_viewed— tracks the last pie detail page viewedlast_order_date— stores the date of the last order
Viewing Caches and Databases
The Application panel also exposes two additional client-side storage mechanisms: Cache Storage and IndexedDB.
Cache Storage:
Entries in Cache Storage are visible under Cache Storage in the Application panel’s left sidebar. Each named cache (e.g., pie-cache) shows all cached requests.
Creating a cache from the console (create_cache.js):
// Run this in the DevTools console to populate the cache
const cache = await caches.open('pie-cache');
cache.add(new Request('/api/pies'));
cache.add(new Request('/about.html'));
Viewing cache contents:
- Click a cache name to see its contents.
- Each row represents one cached URL.
- Clicking a row shows a preview of the cached response below.
- Use the filter box to narrow down entries (e.g., typing
apishows only API requests). - Delete a cached item: select it + click Delete Selected.
IndexedDB:
IndexedDB is a NoSQL database in the browser for storing structured data. It is also visible in the Application panel sidebar under IndexedDB.
Creating an IndexedDB database (create_pies_db.js):
const dbName = "pies_db";
const request = indexedDB.open(dbName, 2);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Create an object store named "pies" with pieID as the key
const objectStore = db.createObjectStore("pies", { keyPath: "pieID" });
// Define indexes for efficient queries
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("description", "description", { unique: false });
objectStore.createIndex("imageURL", "imageURL", { unique: false });
objectStore.createIndex("price", "price", { unique: false });
// Populate the store after creation
objectStore.transaction.oncomplete = (event) => {
const pieObjectStore = db
.transaction("pies", "readwrite")
.objectStore("pies");
pieData.forEach((pie) => {
pieObjectStore.add(pie);
});
};
};
Viewing IndexedDB in DevTools:
- Expand IndexedDB in the left sidebar.
- Expand the database (e.g.,
pies_db) to see its object stores. - Click an object store (e.g.,
pies) to view all stored objects in a grid. - Click a row to see the object’s full properties and values below.
- Click an index name in the left sidebar to sort and view data by that index.
- Delete an individual object: select it + Delete Selected button.
- Delete the entire database: click the database name + Delete database button.
Clearing all site data at once:
In the Application panel, click Storage (at the top of the left sidebar). This presents a checklist of all storage types. Check everything you want to remove (Local Storage, Session Storage, IndexedDB, Cookies, Cache Storage, etc.) and click Clear site data. This gives your app a completely clean slate for debugging storage-related issues.
Module 8: Optimizing Website Performance
Generating a Lighthouse Report
Lighthouse is an open-source automated performance and quality analysis tool for web pages. It can run from within DevTools, from the command line, or as a Node module.
What Lighthouse evaluates:
| Category | What it measures |
|---|---|
| Performance | Page load speed, Core Web Vitals |
| Accessibility | Screen reader support, ARIA, alt text, contrast |
| Best Practices | HTTPS, modern APIs, console errors |
| SEO | Search engine optimization signals |
| Progressive Web App | PWA checklist compliance |
Running a Lighthouse report:
- Open DevTools and click the Lighthouse panel (far right of the panel list).
- Choose the categories you want to analyze (leave all checked for a first run).
- Click Analyze page load.
- Lighthouse runs the analysis (this takes a moment).
- A detailed report appears within DevTools.
Interpreting the report:
- Summary scores at the top (0–100 scale per category).
- Scrolling down reveals detailed findings for each category.
- Each finding includes:
- An icon indicating pass (✅), warn (⚠️), or fail (❌)
- A description of the issue
- Expandable details with specific affected elements
Example findings on the demo site:
- Missing
altattributes on images — Expand the Accessibility section to see exactly which image elements fail this test. - Performance is high — Expected, since the site runs locally on a fast desktop.
Exporting the report:
Use the options menu (pinned top-right of the report) to:
- Print a summary or detailed version
- Save as HTML
- Save as raw JSON data
Using the Performance Panel
The Performance panel provides deep profiling of actual site usage — not just the initial page load. Use it when you need to investigate specific runtime performance problems.
flowchart LR
A["Open Performance Panel"] --> B["Configure Settings\n(throttling, etc.)"]
B --> C["Click Record ⏺"]
C --> D["Interact with the site\n(click links, scroll, etc.)"]
D --> E["Click Stop ⏹"]
E --> F["Analyze the timeline"]
F --> G["Zoom into areas of interest"]
G --> H["Review Summary pane\nfor selected activity"]
Recording a session:
- Open the Performance panel.
- Optionally configure throttling via the gear icon.
- Click the Record button (⏺) on the left of the toolbar.
- Interact with the site (e.g., navigate to pages, click links).
- Click Stop (⏹).
DevTools immediately produces a detailed analysis.
Performance panel layout:
- Top: Overview timeline (FPS, CPU, Network activity over the full recording)
- Middle: Flame chart — stacked colored boxes showing execution timeline (main thread, rendering, painting, etc.)
- Bottom: Summary pane with totals for the entire recording or the selected time range
Zooming and navigating:
- Click and drag on the overview timeline to select a time range. The middle section updates to show only that window.
- Use the scroll wheel on the middle section to zoom in further.
- Hover over any box in the flame chart to see a popup with timing details.
- Click a box to load its information into the Summary pane.
Throttling for realistic testing:
Configure CPU and network throttling in the Performance panel settings (gear icon) before recording to simulate the experience of users on slower devices.
Experimenting with Preview Features
Chrome DevTools is under constant development. Occasionally, preview (experimental) features are included — recognizable by a small beaker icon (🧪) beside them in the panel list or menus.
Characteristics of preview features:
- They change frequently, sometimes dramatically.
- Some are eventually promoted to stable features; others are removed.
- Each preview feature panel includes basic documentation about what it does.
- A feedback section at the bottom of preview feature panels explains where to send feedback.
Caution: Do not rely on preview features for production debugging workflows, as they may not be available or may behave differently in subsequent Chrome versions.
To explore them, click any beaker-icon panel when you want to stay on the cutting edge and help shape the future of DevTools.
Course Wrap-up
Chrome DevTools is a powerful and tremendously helpful tool. Understanding even the most commonly used features significantly accelerates the process of finding and fixing bugs.
Key takeaways:
| Topic | What you can do |
|---|---|
| Elements panel | Examine and modify the live DOM and CSS styles |
| Console | Read log messages, run JavaScript, monitor live expressions |
| Sources panel | Set breakpoints, step through code, inspect variables and call stack |
| Device Mode | Simulate different devices, viewports, throttling, sensors |
| Network panel | Inspect HTTP requests and responses, diagnose communication failures |
| Application panel | View and edit local storage, cookies, cache, and IndexedDB |
| Lighthouse | Audit performance, accessibility, SEO, and best practices |
| Performance panel | Profile real usage and identify performance bottlenecks |
Suggested learning path:
flowchart LR
A["Start: Open DevTools\nF12"] --> B["Elements Panel\nInspect DOM + CSS"]
B --> C["Console Panel\nLog messages + JS REPL"]
C --> D["Sources Panel\nBreakpoints + Debugger"]
D --> E["Network Panel\nHTTP Traffic"]
E --> F["Application Panel\nStorage inspection"]
F --> G["Lighthouse + Performance\nOptimization"]
G --> H["Advanced:\nSource Maps, Snippets,\nLive Expressions"]
Official DevTools documentation: https://developer.chrome.com/docs/devtools/
The documentation is well-written and comprehensive, covering details about existing features, notes about new features, and helpful tips. Combining the hands-on experience from this course with the official documentation will give you the perspective needed to continue discovering new features as you need them.
We all create bugs — getting better at fixing them faster is part of the job.
Quick Reference: Keyboard Shortcuts
| Action | Windows / Linux | macOS |
|---|---|---|
| Open DevTools (last used panel) | F12 | Command+Option+I |
| Open DevTools to Elements panel | Ctrl+Shift+C | Command+Shift+C |
| Open Command Menu | Ctrl+Shift+P | Command+Shift+P |
| Open Console | Ctrl+Shift+J | Command+Option+J |
| Close DevTools | F12 or Ctrl+Shift+I | Command+Option+I |
| Debugger controls | ||
| Continue execution | F8 | F8 |
| Step over | F10 | F10 |
| Step into | F11 | F11 |
| Step out | Shift+F11 | Shift+F11 |
| Console | ||
| Confirm live expression | Ctrl+Enter | Command+Enter |
| Run snippet | Ctrl+Enter | Command+Enter |
| Elements panel search | Ctrl+F | Command+F |
Search Terms
debugging · sites · chrome · devtools · html · css · web · fundamentals · frontend · development · panel · performance · viewing · api · communication · console · device · dom · editing · javascript · local · network