search
HomeWeb Front-endJS TutorialHow to master JavaScript debugging for web apps

Written by Ivy Walobwa✏️

As your web app grows in complexity, it becomes essential to master the art of debugging.

Effective JavaScript debugging involves more than just fixing errors. It requires an understanding of how your code works under the hood to ensure your app runs smoothly and delivers the best user experience.

Minified code, which is the version of your code that reaches users in production, is optimized for performance. However, minified code can be a nightmare to debug. When users encounter errors, reproducing and diagnosing issues in minified code is often challenging.

However, with the right tools, JavaScript debugging can become much easier. This article will explore how to leverage source maps to debug minified code and dive into other techniques using Chrome DevTools to efficiently identify and resolve issues in your web app.

Example app

We’ll work on a simple app that increments a count and logs it onto the console. This app demonstrates how minified code can make debugging tricky and how source maps can help simplify the process.

Create the .js files below and add the code snippets as shown:

1. src/counterCache.js

export const countCache = { 
     previousCount: 0, 
     currentCount: 0, 
     totalCount: 0 
}
export function updateCache(currentCount, previousCount) { 
     countCache.currentCount = currentCount; 
     countCache.previousCount = previousCount; c
     ountCache.totalCount = countCache.totalCount + countCache.currentCount; 
}

2.src/counter.js:

import { updateCache } from './counterCache.js';
let count = 0; 
export function incrementCounter() 
     { count += 1; 
     const previousCount = count; 
     updateCache(count, previousCount); 
}

3.src/index.js:

import { incrementCounter } from './counter';
import { countCache } from './counterCache';
const button = document.createElement('button');
const previousElement = document.getElementById('previous');
const currentElement = document.getElementById('current');
const totalElement = document.getElementById('total');
button.innerText = 'Click me';
document.body.appendChild(button);
button.addEventListener('click', () => {
     incrementCounter();
     previousElement.innerText = countCache.previousCount;
     currentElement.innerText = countCache.currentCount;
     totalElement.innerText = countCache.total();
});

In your package.json file, add the webpack packages as shown below then run npm i to install them. We'll use webpack as part of the build process to generate minified code for production:

  "devDependencies": {
    "webpack": "^5.96.1",
    "webpack-cli": "^5.1.4"
  }

To enable code minification, add a webpack.config.js file with the following snippet. Setting the mode to production tells webpack to apply optimizations such as modification:

 const path = require('path');
    module.exports = {
        mode: 'production', // Enables optimizations like minification and tree-shaking
        entry: './src/index.js', // Specifies the entry point of your application
        output: {
            path: path.resolve(__dirname, 'dist'),// Defines the output directory for bundled files
            filename: 'bundle.js',// Specifies the name of the bundled file
        },
    };

Now run npx webpack to bundle and minify your code. The dist/bundle.js file is generated with content as shown below. Minification obscures variable and function names, and removes unnecessary characters like whitespace, comments, and unused code, making the output file smaller and faster to load:

(()=>{"use strict";const t={};let e=0;const n=document.createElement("button"),o=document.getElementById("previous"),u=document.getElementById("current"),r=document.getElementById("total");n.innerText="Click me",document.body.appendChild(n),n.addEventListener("click",(()=>{var n,c;e+=1,n=e,c=e,t.currentCount=n,t.previousCount=c,t.totalCount=t.totalCount||0+t.currentCount,o.innerText=t.previousCount,u.innerText=t.currentCount,r.innerText=t.total()}))})();

Next, update the index.html file to reference the bundled output, ensuring your application uses the minified code:



    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Debugging Example</title>
    <link rel="stylesheet" href="styles.css"> 


    <h1 id="Web-Debug-App">Web Debug App</h1>
    <p>Check console for bug</p>
    
Previous count Current count Total count

Finally, run the app and check the console after clicking the button. To preview the app locally, you can use the Live Server extension in VS Code:

app error using minified code

Bundled source file

The error in the console, t.total is not a function, is difficult to interpret. Clicking on the file in the console does not help pinpoint the issue due to the compact and obfuscated nature of minified code. Identifying the root cause of such an error in a large codebase can be frustrating and time-consuming, as the minified code obscures the original logic and context.

8 JavaScript debugging strategies for web apps

Let’s demonstrate eight methods to help make JavaScript debugging a bit easier:

1. Source maps

Source maps are files that map your minified code back to the original source code. They make debugging easier and help investigate issues in production. The file names of source maps end with .map.

To generate source maps using webpack, update the webpack.config.js file as follows:

The devtool: 'source-map' or devtool: 'eval-source-map' line tells webpack to generate an external .map file which maps the minified code back to your original source code. The source map file URL is also added to the minified code in the bundle.js file.

Now run npx webpack. The .map file will generate alongside your minified bundle. Serve the application using a local server, and open it in an Incognito browser window. This prevents browser extensions and cached files from interfering with your debugging process.

With source maps generated, the following observations are made:

  1. The error is linked to the counter.js file, which is the original source code
  2. The source map, bundle.js.map is successfully fetched and is visible under the Developer resources tab
  3. In the Sources tab, the developer tools display the original source code and the problematic line

The exact code and file causing the bug are easy to identify using source maps:

app error from source maps

mapped source code javascript debugging

With the clear error above, we are able to fix the error and access the correct property on countCache.

Our guide on how to use Chrome DevTools should provide a great start. To open the Developer resources tab, click on the More icon, then More tools then Developer resources. This tab allows you to view the source map load status and even load source maps manually:

accessing developer resources tab javascript debugging

The code snippet below fixes the bug on the console. Update your code, then run npx webpack to compile the changes. Once completed, serve the application and view the updated output in the table:

totalElement.innerText = countCache.totalCount;

Clicking the button currently updates the previous count, current count, and total on the table. The previous count is supposed to return the previous value of count and the total count is to return the sum of all count values. At the moment, the previous count displays the current count while the total count is stuck at one.

In the next section, we’ll explore additional JavaScript debugging techniques, such as using breakpoints and stepping through the code, to identify and fix this issue:

web debugging example app output

2. Breakpoints

Breakpoints allow you to pause the execution of your code at specific lines, helping you inspect variables, evaluate expressions, and understand the code flow. Depending on your goal, there are different breakpoints you can use. For instance:

  • Line-of-code — Pauses your code execution at an exact line specified
  • Conditional line-of-code — Pauses execution only when a specified condition is true
  • Logpoint —Doesn’t pause code execution but instead logs a custom message to the console when that line of code is executed

In our sample application, we’ll apply a breakpoint to the incrementCounter function. On the Sources panel, open the counter.js file and click to the left of line six. This sets a line-of-code breakpoint after the count is increased:

setting line of code breakpoint

We’ll set another breakpoint at line five and edit it. To edit our breakpoint, we’ll right-click on the highlighted section and then click on Edit breakpoint:

edit breakpoint javascript debugging

We’ll set the breakpoint type to Logpoint, then enter the message to be logged to the console:

setting logpoint breakpoint

By clicking the button, our application pauses at the line-of-code breakpoint and prints a debug log on the console from the Logpoint set:

app paused line of code breakpoint

From the image we can see the following sections:

  • The breakpoints panel — Helps manage and toggle your breakpoints. Currently, we have two breakpoints added, on lines five and six. These breakpoints can be enabled or disabled from the panel
  • The scope panel — Crucial for inspecting variable states and values at the current breakpoint
  • Debugging controls This allows you to navigate through your code step by step. The controls are: resume, step over, step into, step out, and step

With this, we can debug our app further.

3. Scope panel

The scope panel can be effective for JavaScript debugging, as it allows you to see variables from the original source:

scope panel javascript debugging

We can see the following scope variables:

  1. Local - These are variables defined within the currently executing function
  2. Closure - These variables are captured from the executing function's outer block or script scopes
  3. Closure - This type of variable is obtained from generated scopes eg., using module files
  4. Global - These are variables available throughout the application

From the scope panel and the log point breakpoint, we can see that the current count is one while the count before the increase is zero. We therefore need to store the count before the increment as the previous count.

4. Stepping through code (_s_tep into, step over, step out)

Stepping through your code involves navigating through the program in different ways during JavaScript debugging:

  • Step into - Allows you to enter into a function call and examine the code inside that function
  • Step over - Skips over the function call, executing it without diving in
  • Step out - Allows you to return to the caller context of a function if you stepped into it

You can use the debug controls to step through your code. The Step control enables you to run your code, one line at a time. Clicking on Step will execute line six and move to line seven. Note how the value of previousCount changes in the scope:

stepping through code

The Step over control allows you to execute a function without going through it line by line:

stepping over code

The Step into control allows you to go into a function. In the function, you can step through the code line by line or Step out of the function as shown below. Stepping out of the function will finish the execution of the remaining lines:

step into and step out of a line of code

To fix our issue, we’ll update the code as shown below. This now displays the previous count on the table correctly:

export const countCache = { 
     previousCount: 0, 
     currentCount: 0, 
     totalCount: 0 
}
export function updateCache(currentCount, previousCount) { 
     countCache.currentCount = currentCount; 
     countCache.previousCount = previousCount; c
     ountCache.totalCount = countCache.totalCount + countCache.currentCount; 
}

5. The call stack

The call stack shows the sequence of function calls that led to the current point in the code.

Add a new breakpoint in the counterCache.js file as shown, then click the button. Observe the call stack panel:

call stack panel

There are three function calls made when the app executes line six of counterCache.js. To observe the flow of any functions in the stack, you can restart their execution using Restart frame, as shown below:

restart frame call stack

6. Ignoring scripts

When debugging, you may wish to ignore certain scripts during your workflow. This helps skip over the complexities of code from libraries or code generators. In our case, we want to ignore the counter.js script while debugging.

On the Page tab, right-click on the file to be ignored and add the script to the ignore list:

add script ignore list

Running the app and pausing on the breakpoint, we can see the incrementCounter function is now ignored on the call stack. You can hide or show the ignored frames:

ignored frames call stack

You can group your files in the Pages tab for easier navigation as shown in the image below:

grouping source files

7. Watch expressions

Watch expressions let you track specific variables or expressions as your code executes, helping you monitor changes in real time. You can add expressions like countCache to monitor the value as you step through the code:

adding watch expressions

8. Debugging code snippets

To try to fix the bug with the total count, you may run code snippets on the console to understand the logical error. When debugging code that you run repeatedly on the console, you can make use of Snippets.

On the Snippets tab, add a sample debug script, save the script then click Enter to run the script:

javascript debugging snippet

You can observe that the expression with the bug needs to be rearranged to fix the issue:

export const countCache = { 
     previousCount: 0, 
     currentCount: 0, 
     totalCount: 0 
}
export function updateCache(currentCount, previousCount) { 
     countCache.currentCount = currentCount; 
     countCache.previousCount = previousCount; c
     ountCache.totalCount = countCache.totalCount + countCache.currentCount; 
}

You can explore additional resources on debugging web apps such as this article on debugging React apps with React DevTools, which offers valuable insights into debugging React-based applications. Additionally, this guide on debugging Node.js with Chrome DevTools provides tips for debugging server-side JavaScript using watchers and other advanced DevTools features. These resources can complement the techniques discussed here and broaden your understanding of debugging web apps.

Conclusion

This tutorial explored debugging minified code busing source maps and Chrome DevTools. By generating source maps, we mapped minified code back to its original source, making it easier to debug our web app. Chrome DevTools further enhanced the JavaScript debugging process with methods such as breakpoints, stepping through code, watch expressions, and more.

With these tools, developers can efficiently debug and optimize their applications, even when dealing with complex, minified codebases. The complete code for this project can be found on GitHub.


Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.

NPM:

import { updateCache } from './counterCache.js';
let count = 0; 
export function incrementCounter() 
     { count += 1; 
     const previousCount = count; 
     updateCache(count, previousCount); 
}

Script Tag:

import { incrementCounter } from './counter';
import { countCache } from './counterCache';
const button = document.createElement('button');
const previousElement = document.getElementById('previous');
const currentElement = document.getElementById('current');
const totalElement = document.getElementById('total');
button.innerText = 'Click me';
document.body.appendChild(button);
button.addEventListener('click', () => {
     incrementCounter();
     previousElement.innerText = countCache.previousCount;
     currentElement.innerText = countCache.currentCount;
     totalElement.innerText = countCache.total();
});

3.(Optional) Install plugins for deeper integrations with your stack:

  • Redux middleware
  • ngrx middleware
  • Vuex plugin

Get started now.

The above is the detailed content of How to master JavaScript debugging for web apps. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
From Websites to Apps: The Diverse Applications of JavaScriptFrom Websites to Apps: The Diverse Applications of JavaScriptApr 22, 2025 am 12:02 AM

JavaScript is widely used in websites, mobile applications, desktop applications and server-side programming. 1) In website development, JavaScript operates DOM together with HTML and CSS to achieve dynamic effects and supports frameworks such as jQuery and React. 2) Through ReactNative and Ionic, JavaScript is used to develop cross-platform mobile applications. 3) The Electron framework enables JavaScript to build desktop applications. 4) Node.js allows JavaScript to run on the server side and supports high concurrent requests.

Python vs. JavaScript: Use Cases and Applications ComparedPython vs. JavaScript: Use Cases and Applications ComparedApr 21, 2025 am 12:01 AM

Python is more suitable for data science and automation, while JavaScript is more suitable for front-end and full-stack development. 1. Python performs well in data science and machine learning, using libraries such as NumPy and Pandas for data processing and modeling. 2. Python is concise and efficient in automation and scripting. 3. JavaScript is indispensable in front-end development and is used to build dynamic web pages and single-page applications. 4. JavaScript plays a role in back-end development through Node.js and supports full-stack development.

The Role of C/C   in JavaScript Interpreters and CompilersThe Role of C/C in JavaScript Interpreters and CompilersApr 20, 2025 am 12:01 AM

C and C play a vital role in the JavaScript engine, mainly used to implement interpreters and JIT compilers. 1) C is used to parse JavaScript source code and generate an abstract syntax tree. 2) C is responsible for generating and executing bytecode. 3) C implements the JIT compiler, optimizes and compiles hot-spot code at runtime, and significantly improves the execution efficiency of JavaScript.

JavaScript in Action: Real-World Examples and ProjectsJavaScript in Action: Real-World Examples and ProjectsApr 19, 2025 am 12:13 AM

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.

JavaScript and the Web: Core Functionality and Use CasesJavaScript and the Web: Core Functionality and Use CasesApr 18, 2025 am 12:19 AM

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

Understanding the JavaScript Engine: Implementation DetailsUnderstanding the JavaScript Engine: Implementation DetailsApr 17, 2025 am 12:05 AM

Understanding how JavaScript engine works internally is important to developers because it helps write more efficient code and understand performance bottlenecks and optimization strategies. 1) The engine's workflow includes three stages: parsing, compiling and execution; 2) During the execution process, the engine will perform dynamic optimization, such as inline cache and hidden classes; 3) Best practices include avoiding global variables, optimizing loops, using const and lets, and avoiding excessive use of closures.

Python vs. JavaScript: The Learning Curve and Ease of UsePython vs. JavaScript: The Learning Curve and Ease of UseApr 16, 2025 am 12:12 AM

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

Python vs. JavaScript: Community, Libraries, and ResourcesPython vs. JavaScript: Community, Libraries, and ResourcesApr 15, 2025 am 12:16 AM

Python and JavaScript have their own advantages and disadvantages in terms of community, libraries and resources. 1) The Python community is friendly and suitable for beginners, but the front-end development resources are not as rich as JavaScript. 2) Python is powerful in data science and machine learning libraries, while JavaScript is better in front-end development libraries and frameworks. 3) Both have rich learning resources, but Python is suitable for starting with official documents, while JavaScript is better with MDNWebDocs. The choice should be based on project needs and personal interests.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

PhpStorm Mac version

PhpStorm Mac version

The latest (2018.2.1) professional PHP integrated development tool

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Powerful PHP integrated development environment