Welcome to Your Debugging Toolkit
Chrome DevTools is like having X-ray vision for your web applications. Just as a doctor uses advanced imaging to diagnose issues inside the human body, developers use DevTools to peer inside their running applications, examine the code execution, and diagnose problems. Today, we'll learn how to become expert diagnosticians for our JavaScript applications!
Opening Chrome DevTools
There are several ways to open DevTools:
- F12 (Windows/Linux) or Cmd+Opt+I (Mac)
- Right-click on any element and select "Inspect"
- Menu → More Tools → Developer Tools
Pro tip: Ctrl+Shift+I (Windows/Linux) or Cmd+Opt+J (Mac) opens DevTools directly to the Console panel.
The DevTools Panels
Today we'll focus on the Console and Sources panels for JavaScript debugging.
The Console Panel
The Console is your JavaScript playground and error reporting center:
Console Methods
// Basic logging
console.log('Simple message');
console.info('Information message');
console.warn('Warning message');
console.error('Error message');
// Formatting output
console.log('%c Styled message', 'color: blue; font-size: 20px');
console.log('User %s has %d points', 'Alice', 150);
// Object inspection
const user = { name: 'Alice', age: 30, role: 'admin' };
console.log(user);
console.table(user);
console.dir(user);
// Grouping related logs
console.group('User Details');
console.log('Name:', user.name);
console.log('Age:', user.age);
console.log('Role:', user.role);
console.groupEnd();
// Timing operations
console.time('fetchData');
fetchData().then(() => {
console.timeEnd('fetchData');
});
// Counting occurrences
function processItem(item) {
console.count('processItem called');
// Process the item
}
// Assertions
console.assert(user.age > 0, 'Age must be positive');
// Stack traces
function outer() {
inner();
}
function inner() {
console.trace('Trace from inner function');
}
outer();
Console Shortcuts
$_- Returns the most recently evaluated expression$0-$4- Returns recently selected elements in Elements panel$(selector)- Shorthand for document.querySelector()$$(selector)- Shorthand for document.querySelectorAll()clear()- Clears the console
The Sources Panel - Your Debugging Command Center
The Sources panel is where real debugging happens. It includes:
- File navigator (left)
- Code editor (center)
- Debugging panels (right)
Setting Breakpoints
function calculateDiscount(price, discountPercent) {
// Click on line numbers to set breakpoints
const discount = price * (discountPercent / 100);
const finalPrice = price - discount;
return {
originalPrice: price,
discount: discount,
finalPrice: finalPrice
};
}
// Different types of breakpoints:
function processOrder(order) {
// 1. Line breakpoint - stops on this line
console.log('Processing order:', order.id);
// 2. Conditional breakpoint - stops only when condition is true
// Right-click line number → Add conditional breakpoint
if (order.total > 1000) { // Set condition: order.total > 1000
applyDiscount(order);
}
// 3. Logpoint - logs without stopping execution
// Right-click line number → Add logpoint
console.log('Order status:', order.status); // Logpoint message
// 4. XHR/fetch breakpoint - stops on network requests
fetch('/api/orders/' + order.id)
.then(response => response.json())
.then(data => console.log(data));
}
Debugging Controls
When execution pauses at a breakpoint, you have several controls:
Watch Expressions
Add expressions to monitor their values as you debug:
// Example function to debug
function shoppingCart(items) {
let total = 0;
let itemCount = 0;
// Add these to Watch panel:
// total
// itemCount
// items.length
// total / itemCount
for (let item of items) {
total += item.price * item.quantity;
itemCount += item.quantity;
}
return {
total: total,
itemCount: itemCount,
average: total / itemCount
};
}
Call Stack and Scope
Understanding the call stack and variable scope is crucial for debugging:
function outer() {
const outerVar = 'I am from outer';
function middle() {
const middleVar = 'I am from middle';
function inner() {
const innerVar = 'I am from inner';
// Set breakpoint here
console.log(outerVar); // Accessible
console.log(middleVar); // Accessible
console.log(innerVar); // Accessible
debugger; // Programmatic breakpoint
}
inner();
}
middle();
}
outer();
// When paused at debugger:
// Call Stack shows: inner → middle → outer → (anonymous)
// Scope shows: Local, Closure (middle), Closure (outer), Global
Advanced Debugging Techniques
Event Listener Breakpoints
// Set up event listeners
document.getElementById('submitBtn').addEventListener('click', function(e) {
// DevTools can break on specific event types
console.log('Button clicked');
submitForm();
});
// In DevTools:
// 1. Go to Sources panel
// 2. Expand Event Listener Breakpoints
// 3. Check 'Mouse → click'
// Now DevTools will pause whenever a click event fires
DOM Breakpoints
// You can set breakpoints on DOM modifications
const element = document.getElementById('myElement');
// In DevTools:
// 1. Right-click element in Elements panel
// 2. Break on → Subtree modifications
// 3. Now DevTools pauses when children are added/removed
// This will trigger the breakpoint
element.innerHTML = '<p>New content</p>';
Exception Breakpoints
// Enable "Pause on exceptions" in DevTools
function riskyOperation() {
try {
// This will throw an error
JSON.parse('invalid json');
} catch (e) {
console.error('Caught error:', e);
}
}
// With "Pause on exceptions" enabled:
// - Pauses when exception is thrown
// - Even if it's caught later
// - Helps find the source of errors
Debugging Asynchronous Code
Async Call Stack
// DevTools can track async operations
async function fetchUserData() {
console.log('Fetching user data...');
try {
const response = await fetch('/api/user');
const data = await response.json();
console.log('User data received');
return data;
} catch (error) {
console.error('Fetch failed:', error);
throw error;
}
}
// Enable "Async" in Call Stack
async function processUser() {
const userData = await fetchUserData();
// Set breakpoint here
// Call Stack will show the async chain
console.log('Processing user:', userData.name);
}
Promise Debugging
// Debugging promises
function delay(ms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(`Waited ${ms}ms`);
}, ms);
});
}
// Debug promise chains
delay(1000)
.then(result => {
console.log(result);
return delay(500);
})
.then(result => {
// Set breakpoint here
console.log(result);
return delay(250);
})
.catch(error => {
console.error('Promise failed:', error);
});
Performance Debugging
Performance Timing
// Measure code performance
function performanceTest() {
// Using console.time
console.time('Operation');
heavyComputation();
console.timeEnd('Operation');
// Using performance.now
const start = performance.now();
heavyComputation();
const end = performance.now();
console.log(`Operation took ${end - start}ms`);
// Using Performance API
performance.mark('startOperation');
heavyComputation();
performance.mark('endOperation');
performance.measure('My Operation', 'startOperation', 'endOperation');
// View in Performance panel
const measures = performance.getEntriesByType('measure');
console.table(measures);
}
function heavyComputation() {
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.sqrt(i);
}
return result;
}
Memory Debugging
Finding Memory Leaks
// Common memory leak patterns
let leakyArray = [];
function createLeak() {
// This creates a memory leak
const largeObject = {
data: new Array(1000000).fill('*'),
id: Date.now()
};
leakyArray.push(largeObject);
// Objects keep accumulating in memory
}
// Better approach
function noLeak() {
const largeObject = {
data: new Array(1000000).fill('*'),
id: Date.now()
};
// Process the object
processData(largeObject);
// Object can be garbage collected after function exits
}
// Detecting leaks in DevTools:
// 1. Take heap snapshot
// 2. Perform action that might leak
// 3. Take another snapshot
// 4. Compare snapshots
Network Debugging
Debugging Network Requests
// Monitor network activity
async function fetchData() {
try {
// This request will appear in Network panel
const response = await fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ query: 'test' })
});
// Check response status
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Data received:', data);
} catch (error) {
console.error('Network error:', error);
}
}
// Network panel features:
// - Filter requests by type
// - Examine request/response headers
// - Preview response data
// - Check timing information
// - Simulate slow connections
Real-World Debugging Scenarios
Scenario 1: Debugging a Form Submission
function validateForm(formData) {
const errors = [];
// Set breakpoint here to inspect formData
if (!formData.email || !formData.email.includes('@')) {
errors.push('Invalid email');
}
if (!formData.password || formData.password.length < 8) {
errors.push('Password must be at least 8 characters');
}
// Watch expression: errors.length
return errors;
}
function submitForm(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
// Conditional breakpoint: errors.length > 0
const errors = validateForm(data);
if (errors.length > 0) {
console.error('Validation errors:', errors);
displayErrors(errors);
} else {
console.log('Form is valid, submitting...');
sendToServer(data);
}
}
Scenario 2: Debugging Async Data Flow
async function loadDashboardData() {
try {
console.group('Loading dashboard data');
// Set breakpoint to examine each step
const user = await fetchUser();
console.log('User loaded:', user);
const permissions = await fetchPermissions(user.id);
console.log('Permissions loaded:', permissions);
const dashboardData = await fetchDashboard(user.id, permissions);
console.log('Dashboard data loaded:', dashboardData);
console.groupEnd();
renderDashboard(dashboardData);
} catch (error) {
// Exception breakpoint will pause here
console.error('Dashboard loading failed:', error);
showErrorMessage('Failed to load dashboard');
}
}
Debugging Tips and Tricks
- Black Boxing: Right-click files in Sources to black box third-party libraries
- Workspace: Edit files directly in DevTools and save changes
- Snippets: Save frequently used debugging code snippets
- Command Menu: Ctrl+Shift+P for all DevTools commands
- Network Throttling: Simulate slow connections for testing
- Device Mode: Test responsive designs with device emulation
- Coverage: Find unused JavaScript and CSS
Practice Exercises
Exercise 1: Debug This Shopping Cart
// This code has bugs - use DevTools to find and fix them
function ShoppingCart() {
this.items = [];
this.total = 0;
}
ShoppingCart.prototype.addItem = function(item) {
this.items.push(item);
this.total += item.price; // Bug: doesn't account for quantity
};
ShoppingCart.prototype.removeItem = function(itemId) {
const index = this.items.findIndex(item => item.id === itemId);
if (index > 0) { // Bug: should be >= 0
const item = this.items[index];
this.total -= item.price * item.quantity;
this.items.splice(index, 1);
}
};
// Test the cart
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: 'Book', price: 20, quantity: 2 });
cart.addItem({ id: 2, name: 'Pen', price: 5, quantity: 1 });
cart.removeItem(1);
console.log('Total:', cart.total); // Should be 5, but isn't
Exercise 2: Debug Async Code
// Find why this async function sometimes fails
async function fetchDataWithRetry(url, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
lastError = error;
console.log(`Attempt ${i + 1} failed`);
}
}
throw lastError; // Bug: lastError might be undefined
}
// Test with a failing URL
fetchDataWithRetry('https://api.invalid.com/data')
.then(data => console.log('Success:', data))
.catch(error => console.error('Failed:', error));
Key Takeaways
- Chrome DevTools is an essential debugging tool for JavaScript developers
- The Console panel is great for quick debugging and experimentation
- The Sources panel provides powerful debugging capabilities with breakpoints
- Learn keyboard shortcuts to debug more efficiently
- Use conditional breakpoints and logpoints for targeted debugging
- The Call Stack and Scope panels help understand code execution
- Network and Performance panels help debug non-code issues
- Practice debugging regularly to improve your skills