Critical Rendering Path (CRP) in JavaScript

  • Let me explain what happens behind the scenes when you load a webpage in your browser.

What is Critical Rendering Path?

  • Simple Definition: The Critical Rendering Path is the sequence of steps the browser takes to convert HTML, CSS, and JavaScript into pixels on the screen.

Real-world Analogy: Imagine you're building a house:

  1. First, you create the blueprint (HTML parsing)
  2. Then, you decide the colors and interior design (CSS parsing)
  3. You build the structure (DOM + CSSOM)
  4. You add furniture and decorations (Render Tree)
  5. You measure where everything goes (Layout)
  6. Finally, you paint and finish everything (Paint)

The browser does something similar to display your webpage!


The 6 Steps of Critical Rendering Path

Step 1: DOM Construction (Document Object Model)

What happens: Browser reads HTML and converts it into a tree structure called the DOM.


    <!DOCTYPE html>
    <html>

    <head>
        <title>My Page</title>
    </head>

    <body>
        <div class="container">
            <h1>Hello World</h1>
            <p>Welcome to my site</p>
        </div>
    </body>

    </html>

Browser converts this to:


    html
    └── head
        └── title
    └── body
        └── div.container
            ├── h1
            └── p

  • Important: Browser does this incrementally (line by line). It doesn't wait for the entire HTML to download.


Step 2: CSSOM Construction (CSS Object Model)

What happens: Browser reads CSS and creates a tree structure similar to DOM, called CSSOM.


    body {
        font-size: 16px;
    }

    .container {
        width: 100%;
        padding: 20px;
    }

    h1 {
        color: blue;
        font-size: 32px;
    }

    p {
        color: gray;
    }
 

Browser creates CSSOM:


    body (font-size: 16px)
    └── .container (width: 100%, padding: 20px)
        ├── h1 (color: blue, font-size: 32px, inherits font-size from body)
        └── p (color: gray, inherits font-size from body)

  • Critical Point: CSS is render-blocking. The browser won't render anything until it has processed all CSS. Why? Because CSS rules can override each other, so the browser needs the complete picture.


Step 3: Render Tree Construction

  • What happens: Browser combines DOM and CSSOM to create a Render Tree. This tree contains only the elements that will be visible on the screen.

Example:


    <div>
        <p>Visible paragraph</p>
        <p style="display: none;">Hidden paragraph</p>
        <span>Visible span</span>
    </div>

Render Tree only includes:


    div
    ├── p (Visible paragraph)
    └── span (Visible span)

  • The hidden paragraph is NOT in the Render Tree because it won't be displayed.

Elements excluded from Render Tree:

  • display: none elements
  • <head> and its children
  • <script> tags
  • <meta> tags

Step 4: Layout (Reflow)

  • What happens: Browser calculates the exact position and size of each element on the screen.
  • Real-world Analogy: You know what furniture you have (Render Tree), now you need to measure and decide where each piece goes in your room.


    <div style="width: 100%">
        <div style="width: 50%">
            <p>Text here</p>
        </div>
    </div>

Browser calculates:

  • If viewport is 1000px wide
  • Outer div = 1000px
  • Inner div = 500px (50% of 1000px)
  • Position of each element (x, y coordinates)

This process is also called "Reflow"


Step 5: Paint

  • What happens: Browser fills in pixels. It paints text, colors, images, borders, shadows - everything visual.

Order matters: Browser paints in layers:

  1. Background colors
  2. Background images
  3. Borders
  4. Children elements
  5. Outlines

Step 6: Compositing

  • What happens: If you have multiple layers (like elements with position: fixed or CSS transforms), the browser combines them in the correct order.


JavaScript's Role in CRP

  • JavaScript can block the entire rendering process. Let's understand how:

Problem 1: Parser-Blocking JavaScript


    <!DOCTYPE html>
    <html>

    <head>
        <title>My Page</title>
        <script src="large-script.js"></script> <!-- BLOCKS HERE -->
    </head>

    <body>
        <h1>Hello World</h1>
    </body>

    </html>

What happens:

  1. Browser starts parsing HTML
  2. Encounters <script> tag
  3. STOPS parsing HTML
  4. Downloads and executes JavaScript
  5. Only then continues parsing HTML

  • Why? Because JavaScript can modify the DOM (using document.write() or manipulating elements), so the browser must execute it before continuing.


Problem 2: JavaScript Waiting for CSS


    <!DOCTYPE html>
    <html>

    <head>
        <link rel="stylesheet" href="styles.css">
        <script src="app.js"></script>
    </head>

    <body>
        <h1>Hello World</h1>
    </body>

    </html>

What happens:

  1. Browser starts downloading CSS
  2. Encounters <script> tag
  3. WAITS for CSS to download and parse
  4. Then executes JavaScript
  5. Then continues parsing HTML

  • Why? Because JavaScript might read computed styles (like getComputedStyle()), so it needs complete CSS information.


Optimization Techniques

1. Async Attribute


    <script src="analytics.js" async></script>

What it does:

  • Downloads script in parallel with HTML parsing
  • Executes script as soon as it's downloaded (might interrupt parsing)
  • Good for scripts that don't depend on DOM or other scripts

Visual Timeline:


    HTML Parsing: ————————————————————————
    Script Download:    ————————
    Script Execute:           ⚡
    HTML Parsing continues: ————————————————


2. Defer Attribute


    <script src="main.js" defer></script>

What it does:

  • Downloads script in parallel with HTML parsing
  • Executes script after HTML parsing is complete
  • Scripts execute in order
  • Perfect for scripts that need complete DOM

Visual Timeline:


    HTML Parsing: ————————————————————————
    Script Download:    ————————
    Script Execute:                        ⚡


3. Placing Scripts at Bottom


    <!DOCTYPE html>
    <html>

    <head>
        <title>My Page</title>
    </head>

    <body>
        <h1>Hello World</h1>
        <p>Content here</p>

        <!-- Scripts at the end -->
        <script src="app.js"></script>
    </body>

    </html>

  • Benefit: HTML is parsed and rendered first, then JavaScript executes.


4. Critical CSS (Inline)


    <!DOCTYPE html>
    <html>

    <head>
        <style>
            /* Inline critical CSS for above-the-fold content */
            body {
                margin: 0;
                font-family: Arial;
            }

            .hero {
                height: 100vh;
                background: blue;
            }
        </style>

        <!-- Load non-critical CSS asynchronously -->
        <link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'">
    </head>

    <body>
        <div class="hero">Welcome</div>
    </body>

    </html>


5. Minification

Before:


    function calculateTotal(price, quantity) {
        const tax = 0.1;
        const subtotal = price * quantity;
        const total = subtotal + (subtotal * tax);
        return total;
    }

After (minified):


    function calculateTotal(p, q) { const t = .1, s = p * q; return s + s * t }

Benefit: Smaller file size = faster download


Real-World Example: Optimized HTML Structure

❌ Bad (Slow Loading):


    <!DOCTYPE html>
    <html>

    <head>
        <link rel="stylesheet" href="styles.css">
        <link rel="stylesheet" href="animations.css">
        <link rel="stylesheet" href="fonts.css">
        <script src="jquery.js"></script>
        <script src="bootstrap.js"></script>
        <script src="app.js"></script>
    </head>

    <body>
        <h1>Welcome</h1>
    </body>

    </html>

Problems:

  • Multiple CSS files block rendering
  • JavaScript blocks parsing
  • No prioritization

✅ Good (Optimized):


    <!DOCTYPE html>
    <html>

    <head>
        <!-- Inline critical CSS -->
        <style>
            body {
                margin: 0;
                font-family: Arial;
            }

            h1 {
                font-size: 2em;
                color: #333;
            }
        </style>

        <!-- Preload important resources -->
        <link rel="preload" href="fonts/main.woff2" as="font" type="font/woff2" crossorigin>

        <!-- Load non-critical CSS asynchronously -->
        <link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'">
    </head>

    <body>
        <h1>Welcome</h1>

        <!-- Scripts with defer at the end -->
        <script src="app.js" defer></script>
    </body>

    </html>

Benefits:
  • Critical styles load immediately
  • JavaScript doesn't block rendering
  • Fonts preload for faster text rendering

Measuring CRP Performance

Key Metrics:

1. First Contentful Paint (FCP)

  • When the first text or image appears
  • Target: Under 1.8 seconds

2. Largest Contentful Paint (LCP)

  • When the largest element appears
  • Target: Under 2.5 seconds

3. Time to Interactive (TTI)

  • When page becomes fully interactive
  • Target: Under 3.8 seconds

How to Measure:

Chrome DevTools:


    // In Console
    performance.getEntriesByType('navigation')[0]

Lighthouse:

  • Open DevTools → Lighthouse tab → Generate report

Practical JavaScript Optimization Example

❌ Bad: Blocking JavaScript


    <script>
        // Heavy computation blocking render
        function fibonacci(n) {
            if (n <= 1) return n;
            return fibonacci(n - 1) + fibonacci(n - 2);
        }

        const result = fibonacci(40); // Takes several seconds!
        console.log(result);
    </script>

    <h1>My Website</h1>

  • Problem: User sees blank screen while JavaScript executes.


✅ Good: Non-Blocking JavaScript


    <h1>My Website</h1>

    <script defer>
        // Run after page renders
        function fibonacci(n) {
            if (n <= 1) return n;
            return fibonacci(n - 1) + fibonacci(n - 2);
        }

        // Or use Web Workers for heavy computation
        setTimeout(() => {
            const result = fibonacci(40);
            console.log(result);
        }, 0);
    </script>

  • Benefit: Page renders immediately, heavy computation runs after.


Understanding Reflow and Repaint

Reflow (Expensive)

Triggered by changes to:

  • Element dimensions (width, height)
  • Position (top, left)
  • Display property
  • Font size
  • Window resize

    // ❌ Bad: Multiple reflows
    const element = document.getElementById('box');
    element.style.width = '100px'; // Reflow
    element.style.height = '100px'; // Reflow
    element.style.margin = '10px'; // Reflow
    // ✅ Good: Single reflow
    const element = document.getElementById('box');
    element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';


Repaint (Less Expensive)

Triggered by changes to:

  • Color
  • Background color
  • Visibility
  • Outline

    element.style.backgroundColor = 'red'; // Only repaint, no reflow


Advanced: Layer Promotion

Some CSS properties create new layers and avoid reflow/repaint:


    .animated-box {
        /* These properties use GPU and don't trigger reflow */
        transform: translateX(100px);
        opacity: 0.5;
        will-change: transform;
        /* Hint to browser to create layer */
    }

Animating with transform is faster than animating position:


    /* ❌ Slow: Causes reflow */
    .box {
        animation: slide 1s;
    }

    @keyframes slide {
        from {
            left: 0;
        }

        to {
            left: 100px;
        }
    }

    /* ✅ Fast: Uses GPU, no reflow */
    .box {
        animation: slide 1s;
    }

    @keyframes slide {
        from {
            transform: translateX(0);
        }

        to {
            transform: translateX(100px);
        }
    }


Summary - The Complete Flow


1. HTML Download
   ↓
2. HTML Parsing → DOM Construction
   ↓ (if CSS found)
3. CSS Download → CSSOM Construction
   ↓ (CSS blocks rendering)
4. Render Tree = DOM + CSSOM
   ↓
5. Layout (Calculate positions/sizes)
   ↓
6. Paint (Fill in pixels)
   ↓
7. Composite (Combine layers)
   ↓
8. Display on Screen ✅

JavaScript can interrupt this flow at any step!


Key Takeaways

  1. CRP is the journey from code to pixels
  2. CSS blocks rendering - browser waits for all CSS before displaying anything
  3. JavaScript blocks parsing - browser stops parsing HTML when it encounters <script>
  4. Use async for independent scripts (analytics)
  5. Use defer for scripts that need DOM (your main app)
  6. Inline critical CSS for above-the-fold content
  7. Minimize reflows by batching DOM changes
  8. Use transform and opacity for animations (GPU-accelerated)

Understanding CRP helps you build faster websites that load and respond quickly!

setInterval function in JavaScript

  •  The `setInterval` function in JavaScript is used to execute a function repeatedly, with a fixed time delay between each call. It is also part of the browser's Web APIs, specifically the `WindowOrWorkerGlobalScope` mixin.
Syntax


    let intervalID = setInterval(function, delay, [arg1, arg2, ...]);

  • function: The function to be executed repeatedly.
  • delay: The time, in milliseconds, to wait between executions of the function.
  • [arg1, arg2, ...]: Optional arguments to pass to the function when it is executed.
Basic Example


    setInterval(() => {
        console.log("This message is displayed every 2 seconds.");
    }, 2000);

  • In this example, the message "This message is displayed every 2 seconds." will be logged to the console every 2 seconds.
Variations and Use Cases

1. Passing Arguments to the Callback Function


    function greet(name) {
      console.log(`Hello, ${name}!`);
    }

    setInterval(greet, 3000, 'Gagan');

  • Here, the `greet` function is called with the argument 'Gagan' every 3 seconds.
2. Cancelling an Interval
  • The `setInterval` function returns a unique identifier (ID) for the interval, which can be used to cancel the interval using the `clearInterval` function.


    let intervalID = setInterval(() => {
      console.log("This will not be displayed after 5 seconds.");
    }, 1000);

    // Cancel the interval after 5 seconds
    setTimeout(() => {
      clearInterval(intervalID);
    }, 5000);

  • In this case, the interval is cancelled after 5 seconds, so the message will stop being displayed.
3. Using Anonymous Functions
  • You can use anonymous functions directly within `setInterval`.


    setInterval(function () {
        console.log("This is an anonymous function.");
    }, 1000);

4. Executing Functions Immediately and Then Repeatedly

  • Sometimes you might want to execute a function immediately and then repeatedly at a specified interval.


    function logMessage() {
      console.log("This message appears immediately and then every 2 seconds.");
    }

    logMessage(); // Execute immediately
    setInterval(logMessage, 2000);

Interview Scenarios and Questions

1. Explain how `setInterval` works.

  • setInterval schedules a function to run repeatedly at a specified interval. It does not block the main thread; instead, it puts the function in the event queue at the specified intervals.
2. How to stop an interval?
  • Use the `clearInterval` function with the interval ID returned by `setInterval`.


    let intervalID = setInterval(() => {
      console.log("Repeating message.");
    }, 1000);

    // Stop the interval
    clearInterval(intervalID);

3. What happens if the delay is set to 0?

  • Setting the delay to 0 will make the function execute repeatedly as fast as possible but still after the current execution context completes.


    setInterval(() => {
      console.log("This will execute as fast as possible.");
    }, 0);

4. Differences between `setInterval` and `setTimeout`.

  • setInterval calls a function repeatedly with a fixed delay between each call, whereas setTimeout calls a function once after a specified delay.


    // Using setInterval
    setInterval(() => {
      console.log("This message repeats every 2 seconds.");
    }, 2000);

    // Equivalent using setTimeout
    function repeatAction() {
      console.log("This message repeats every 2 seconds.");
      setTimeout(repeatAction, 2000);
    }

    setTimeout(repeatAction, 2000);

5. How to handle `this` context in setInterval?

  • When using `setInterval` inside an object method, `this` may not refer to the object as expected. Use arrow functions or `bind` to preserve the `this` context.


    const obj = {
      name: 'Gagan',
      greet: function () {
        setInterval(() => {
          console.log(`Hello, ${this.name}`);
        }, 1000);
      }
    };

    obj.greet(); // Output: Hello, Gagan

6. Creating a stopwatch using `setInterval`.

  • Here's a simple example of a stopwatch using `setInterval`.


    let seconds = 0;
    const display = document.getElementById('display');

    function updateDisplay() {
      display.textContent = `Time elapsed: ${seconds} seconds`;
    }

    setInterval(() => {
      seconds++;
      updateDisplay();
    }, 1000);

Conclusion

  • Understanding `setInterval` and its variations is crucial for handling repetitive tasks in JavaScript. It helps in scheduling repeated actions, creating timers, and managing periodic updates, which are essential for building dynamic and interactive web applications.

async and defer attribute in JavaScript

  • In HTML, the `async` and `defer` attributes can be added to `<script>` tags to control how JavaScript is loaded and executed. They are used to improve the performance and loading time of web pages. Here’s an explanation of each:
async Attribute
  • 1. Loading Behavior: Scripts with the `async` attribute are downloaded in parallel with other resources (such as HTML parsing) without blocking the browser's rendering process.
  • As soon as the script is downloaded, it is executed immediately, even if the HTML parsing is not finished.
  • 2. Execution Order: The execution order is not guaranteed. Scripts with `async` may execute in any order, depending on which one finishes downloading first.
  • 3. Use Case: `async` is suitable for scripts that do not depend on other scripts and do not need to access the DOM during page load, such as analytics or advertising scripts.
defer Attribute
  • 1. Loading Behavior: Scripts with the `defer` attribute are also downloaded in parallel with other resources.
  • However, they are not executed until the HTML parsing is complete.
  • 2. Execution Order: Scripts with `defer` are executed in the order they appear in the document.
  • 3. Use Case: defer is suitable for scripts that need to access the DOM or depend on other scripts. It ensures that the script runs after the entire document has been parsed.
Without `async` or `defer`
  • 1. Loading and Execution: Without these attributes, scripts are loaded and executed immediately in the order they appear in the HTML. This can block the browser from rendering the rest of the page until the script is fully loaded and executed.
  • 2. Effect on Performance: This blocking behavior can significantly delay the rendering of the page, leading to a poor user experience, especially if the scripts are large or the network is slow.
Comparison and Example
  • Here's an example to illustrate the differences:


    <!DOCTYPE html>
    <html>
      <head>
        <title>Async vs Defer</title>
        
        <!-- Blocks HTML parsing -->
        <script src="script1.js"></script>
    
        <!-- Loads in parallel, executes as soon as it's ready -->  
        <script src="script2.js" async></script>
   
        <!-- Loads in parallel, executes after HTML parsing is complete -->
        <script src="script3.js" defer></script>
      </head>
      <body>
        <h1>Hello World</h1>
      </body>
    </html>
   

  • script1.js will block HTML parsing until it is fully loaded and executed.
  • script2.js will be loaded in parallel and executed as soon as it's ready, potentially before script3.js and before the HTML parsing is complete.
  • script3.js will be loaded in parallel but will only execute after the HTML parsing is complete and in the order it appears in the document.
Summary
  • async: Non-blocking, no execution order guarantee, immediate execution after loading.
  • defer: Non-blocking, preserves execution order, execution after HTML parsing.
  • Without both: Blocking, scripts executed immediately in order, can delay page rendering.
  • Using `async` or `defer` appropriately can greatly enhance the performance and responsiveness of a web page.

Phase 3 — Components Deep Dive

Chapter 1 — What We Are Going to Learn and Why In Phase 2 you learned what a component is and how to create one. You know that a component h...