How to Debug JavaScript in Browser: Chrome DevTools Guide

THEJORD Team1 min read
javascriptdebugchromedevelopment

Effective JavaScript debugging: breakpoints, console, network, performance. Practical Chrome DevTools guide.

How to Debug JavaScript in Browser: Chrome DevTools Guide

Introduction to Browser Debugging

Debugging JavaScript in the browser is an essential skill for every web developer. Chrome DevTools provides a powerful suite of tools that can help you identify and fix bugs quickly, optimize performance, and understand how your code executes. This comprehensive guide will walk you through everything you need to know about debugging JavaScript effectively.

Whether you're tracking down a mysterious bug, profiling performance bottlenecks, or simply trying to understand how a piece of code works, mastering DevTools will significantly improve your productivity as a developer.

Opening Chrome DevTools

Before diving into debugging techniques, let's cover the basics of accessing DevTools:

  • Keyboard shortcut (Windows/Linux): F12 or Ctrl + Shift + I
  • Keyboard shortcut (Mac): Cmd + Option + I
  • Right-click menu: Right-click anywhere on the page and select "Inspect"
  • Chrome menu: More tools → Developer tools

DevTools opens as a panel that can be docked to the bottom, right side, or left side of your browser window, or opened as a separate window entirely.

The Console Panel

Basic Console Methods

The Console is often the first tool developers reach for when debugging. Beyond the basic console.log(), there are many powerful methods available:

// Basic logging
console.log('Simple message');
console.log('Value:', myVariable);

// Styled console output
console.log('%cStyled text', 'color: blue; font-size: 20px');

// Warning and error levels
console.warn('This is a warning');
console.error('This is an error');

// Grouping related logs
console.group('User Data');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.groupEnd();

// Table format for arrays/objects
console.table([
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 }
]);

// Timing operations
console.time('fetchData');
await fetchData();
console.timeEnd('fetchData'); // Output: fetchData: 234.5ms

// Stack trace
console.trace('Trace point');

Console Utilities

DevTools provides special utility functions only available in the Console:

// Select elements (like jQuery)
$('selector')      // Returns first matching element
$$('selector')     // Returns all matching elements

// Get the currently selected element in Elements panel
$0                 // Current selection
$1                 // Previous selection

// Monitor function calls
monitor(functionName);

// Copy any value to clipboard
copy(object);

// Get event listeners on an element
getEventListeners($0);

Setting Breakpoints

Line-of-Code Breakpoints

The most common debugging technique is setting breakpoints in your code. When execution reaches a breakpoint, it pauses, allowing you to inspect the current state:

  1. Open the Sources panel
  2. Navigate to your JavaScript file in the file tree
  3. Click on the line number where you want to pause
  4. A blue marker appears, indicating an active breakpoint

Conditional Breakpoints

Sometimes you only want to pause when certain conditions are met:

  1. Right-click on a line number
  2. Select "Add conditional breakpoint"
  3. Enter a JavaScript expression (e.g., user.id === 42)
  4. The breakpoint only triggers when the condition is true
// This breakpoint condition would pause only for specific users
user.role === 'admin' && user.permissions.includes('delete')

Logpoints

Logpoints are like breakpoints that log messages instead of pausing execution—perfect for adding temporary logging without modifying your code:

  1. Right-click on a line number
  2. Select "Add logpoint"
  3. Enter the message to log (can include expressions in curly braces)
// Logpoint example
"User {user.name} clicked button at {new Date().toISOString()}"

DOM Breakpoints

Pause execution when the DOM changes:

  1. In the Elements panel, right-click on an element
  2. Select "Break on" and choose:
    • Subtree modifications: When child elements change
    • Attribute modifications: When attributes change
    • Node removal: When the element is removed

Event Listener Breakpoints

Pause on specific types of events:

  1. In Sources panel, expand "Event Listener Breakpoints"
  2. Check the events you want to break on (e.g., Mouse → click)
  3. When that event fires, execution pauses

Stepping Through Code

Once paused at a breakpoint, use these controls to navigate through your code:

  • Resume (F8): Continue execution until the next breakpoint
  • Step Over (F10): Execute the current line and move to the next
  • Step Into (F11): Enter the function being called
  • Step Out (Shift + F11): Finish the current function and return to caller
  • Step (F9): Step to the next line, entering async operations

The Call Stack

The Call Stack panel shows you the chain of function calls that led to the current point:

// If your code looks like this:
function processOrder(order) {
  validateOrder(order);  // Currently paused here
}

function validateOrder(order) {
  checkInventory(order.items);
}

function checkInventory(items) {
  // Breakpoint here
}

// The call stack shows:
// checkInventory (current)
// validateOrder
// processOrder
// (anonymous) or main entry point

Click on any frame in the call stack to see the code and variables at that point in the execution.

Inspecting Variables and Scope

The Scope Panel

When paused, the Scope panel shows all variables accessible at the current point:

  • Local: Variables in the current function
  • Closure: Variables from enclosing scopes
  • Global: Global variables (window object)

Watch Expressions

Add expressions to watch their values as you step through code:

  1. In the Sources panel, find the "Watch" section
  2. Click the + button
  3. Enter any JavaScript expression
// Useful watch expressions
user.permissions.length
items.filter(i => i.price > 100).length
Date.now() - startTime

Hover to Inspect

While paused, hover over any variable in your code to see its current value. For objects, you can expand them to see all properties.

Network Panel Debugging

The Network panel is essential for debugging API calls and resource loading:

Filtering Requests

  • XHR: Show only AJAX/fetch requests
  • JS: JavaScript files
  • CSS: Stylesheets
  • Img: Images
  • Filter box: Search by URL, method, or status

Inspecting Request Details

Click on any request to see:

  • Headers: Request and response headers
  • Payload: Request body (for POST/PUT requests)
  • Preview: Formatted response (JSON, HTML, images)
  • Response: Raw response body
  • Timing: Detailed timing breakdown

For JSON API responses, you can use our JSON Formatter tool to better visualize and validate complex data structures.

Throttling Network Speed

Test how your app behaves on slow connections:

  1. Open the Network panel
  2. Find the throttling dropdown (usually shows "No throttling")
  3. Select a preset like "Slow 3G" or "Fast 3G"
  4. Or create custom throttling profiles

Performance Debugging

The Performance Panel

Profile your application to find performance bottlenecks:

  1. Open the Performance panel
  2. Click the record button (or press Ctrl/Cmd + E)
  3. Perform the actions you want to profile
  4. Stop recording
  5. Analyze the flame chart and timing data

Identifying Long Tasks

Look for:

  • Red triangles: Long tasks blocking the main thread
  • Yellow bars: JavaScript execution time
  • Purple bars: Rendering/layout time
  • Green bars: Painting time

Memory Profiling

Track down memory leaks:

  1. Open the Memory panel
  2. Take a heap snapshot
  3. Perform actions that might leak memory
  4. Take another snapshot
  5. Compare snapshots to find objects that weren't garbage collected

Debugging Async Code

Async Stack Traces

Modern DevTools can track async operations across setTimeout, Promise, and async/await:

async function fetchUserData(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const user = await response.json();  // Breakpoint here
  return user;
}

// The async call stack shows the full chain:
// fetchUserData (async)
// handleClick
// (anonymous)

Promise Debugging

Enable "Pause on caught exceptions" to debug Promise rejections:

  1. In Sources panel, click the pause icon with the arrow
  2. Choose "Pause on caught exceptions"
  3. Now Promise rejections will pause even if caught

Source Maps

When debugging minified or transpiled code (TypeScript, Babel, Webpack), source maps let you debug the original source:

// webpack.config.js
module.exports = {
  devtool: 'source-map', // Full source maps for development
  // Or 'cheap-module-source-map' for faster builds
};

With source maps enabled, DevTools shows your original TypeScript/ES6+ code instead of the compiled output.

Useful Keyboard Shortcuts

ActionWindows/LinuxMac
Open DevToolsF12 / Ctrl+Shift+ICmd+Option+I
Open ConsoleCtrl+Shift+JCmd+Option+J
Search all filesCtrl+Shift+FCmd+Option+F
Go to fileCtrl+PCmd+P
Go to lineCtrl+GCmd+G
Toggle breakpointCtrl+BCmd+B
Resume executionF8F8
Step overF10F10
Step intoF11F11

Advanced Debugging Techniques

Live Expressions

Pin expressions that update in real-time:

  1. Click the eye icon in the Console toolbar
  2. Enter an expression
  3. Watch it update live without re-executing

Snippets

Save and run reusable debugging scripts:

  1. In Sources panel, go to Snippets tab
  2. Create a new snippet
  3. Write your debugging code
  4. Run with Ctrl/Cmd + Enter
// Example snippet: Log all click handlers
document.querySelectorAll('*').forEach(el => {
  const listeners = getEventListeners(el);
  if (listeners.click) {
    console.log(el, listeners.click);
  }
});

Blackboxing Scripts

Skip stepping through library code:

  1. Right-click on a file in Sources panel
  2. Select "Add script to ignore list"
  3. The debugger will skip over this file when stepping

Debugging Common Issues

Finding Undefined Variables

Use the Console with pause on exceptions:

// Instead of this crashing silently:
const name = user.profile.name;

// Use optional chaining and log:
console.log('user:', user);
console.log('profile:', user?.profile);
const name = user?.profile?.name ?? 'Unknown';

Tracking Event Handlers

Find what's handling an event:

// In console, inspect event listeners
getEventListeners(document.querySelector('#myButton'));

// Or use monitorEvents
monitorEvents(document.querySelector('#myButton'), 'click');
// Now click the button to see events logged

Debugging Regular Expressions

When working with complex patterns, test them first with our Regex Tester tool before implementing in your code. This saves time and helps visualize matches.

Remote Debugging

Debugging Mobile Devices

Debug mobile Chrome on Android:

  1. Enable USB debugging on your Android device
  2. Connect via USB
  3. In Chrome, go to chrome://inspect
  4. Find your device and click "Inspect"

Debugging Node.js

Debug Node.js applications with Chrome DevTools:

# Start Node with inspector
node --inspect app.js

# Or with break on first line
node --inspect-brk app.js

Then open chrome://inspect in Chrome and click "Open dedicated DevTools for Node".

Conclusion

Mastering Chrome DevTools is an investment that pays off every day as a JavaScript developer. From basic console logging to advanced performance profiling, these tools give you deep visibility into how your code executes.

Key takeaways:

  • Use conditional breakpoints and logpoints to debug without modifying code
  • Master the stepping controls to navigate complex code flows
  • Leverage the Network panel to debug API interactions
  • Profile performance to find and fix bottlenecks
  • Use source maps to debug original source code

For more development tools and resources, check out our free online developer tools, including the JSON Formatter for API response debugging and the Regex Tester for pattern validation.

For the official documentation, visit the Chrome DevTools documentation and MDN's guide to browser developer tools.