CONCEPTS - rs-hash/GETTHATJOB GitHub Wiki

Browser Internals and Rendering Lifecycle

This document explains the entire flow of browser internals: how requests are processed, the critical rendering path, DOM, CSSOM, reflow, compositing, the JavaScript event loop, rendering cycle, and performance best practices.


🔹 1. Request Processing & Navigation Lifecycle
  • User Action: Typing URL / clicking link.

  • Networking:

    • DNS resolution → IP address lookup.
    • TCP handshake → secure connection (TLS if HTTPS).
    • HTTP request → headers, cookies, caching validation.
    • Response → status code, headers, HTML payload.
  • Navigation Stages (per [Navigation Timing API]):

    • fetchStart → request initiated.
    • responseEnd → HTML fully received.
  • Parsing begins immediately (streaming): browser does not wait for full HTML to start building DOM.

Best Practice: Optimize server response time (TTFB) & enable caching/CDN.


🔹 2. Critical Rendering Path (CRP)
  • Steps:

    1. Parse HTML → Build DOM.
    2. Parse CSS → Build CSSOM.
    3. Combine DOM + CSSOM → Render Tree.
    4. Layout (Reflow) → calculate positions/sizes.
    5. Paint → draw pixels (backgrounds, borders, text).
    6. Compositing → GPU assembles layers for screen.
  • Blocking Resources:

    • CSS is render-blocking.
    • JavaScript is parser-blocking (unless async/defer).

Best Practice:

  • Minimize render-blocking JS/CSS.
  • Use preload/prefetch for critical assets.
  • Inline critical CSS.

🔹 3. DOM, CSSOM & Render Tree
  • DOM (Document Object Model):

    • Tree structure built from HTML.
    • Nodes represent elements, text, comments.
  • CSSOM (CSS Object Model):

    • Parsed CSS → cascade & inheritance resolved.
    • Style rules mapped to DOM nodes.
  • Render Tree:

    • Combination of DOM + CSSOM.
    • Excludes display:none but includes pseudo-elements.

Best Practice:

  • Avoid deep DOM trees (affects traversal & reflow).
  • Prefer class selectors over heavy descendant selectors.

🔹 4. Layout (Reflow), Paint & Compositing
  • Layout (Reflow):

    • Calculate box metrics (width, height, position).
    • Triggered by DOM changes or style recalculation.
  • Paint:

    • Convert render tree into actual pixels.
    • Draw borders, shadows, text, backgrounds.
  • Compositing:

    • Layers sent to GPU for rasterization.
    • Browser combines layers efficiently for smooth animations.

⚠️ Expensive Operations:

  • table layouts → full reflow.
  • Changing width/height/top/left → layout.
  • Changing color/background → paint.

Best Practice:

  • Prefer transform & opacity for animations (only compositing).
  • Minimize layout thrashing.

🔹 5. JavaScript Execution & Rendering Order
  • Main Thread:

    • Executes JS, builds DOM/CSSOM, handles input, paints.
  • Blocking:

    • <script> without async/defer → blocks parsing.
    • Long JS tasks → freeze rendering.
  • Async/Defer:

    • async: download in parallel, execute ASAP.
    • defer: download in parallel, execute after DOM parsed.

Best Practice:

  • Split large scripts into smaller chunks.
  • Use Web Workers for CPU-heavy work.

🔹 6. Browser Event Loop + Rendering Cycle
  • Event Loop Phases:

    1. Macro-tasks: script, setTimeout, setInterval, I/O.
    2. Micro-tasks: promises, MutationObserver.
    3. Render Opportunity: after microtasks, before next frame.
  • Frame Budget:

    • 60fps target → ~16ms per frame.
    • If tasks >16ms → dropped frames.

Best Practice:

  • Use requestIdleCallback for low-priority tasks.
  • Use requestAnimationFrame for animations.

🔹 7. requestAnimationFrame (rAF)
  • What it does:

    • Tells browser you want to perform an animation.
    • Callback fired before next repaint.
  • Advantages:

    • Synced with refresh rate (60Hz / 120Hz).
    • Prevents jank.
  • Usage:

    function step(timestamp) {
      element.style.transform = `translateX(${timestamp/10}px)`;
      requestAnimationFrame(step);
    }
    requestAnimationFrame(step);

Best Practice:

  • Always use rAF for smooth animations.
  • Cancel unused rAF loops.

🔹 8. Dirtying, Style Recalculation & Forced Synchronous Layout
  • Dirtying:

    • DOM/style changes mark nodes as needing recalculation.
  • Style Recalculation:

    • Triggered when classes/styles change.
  • Forced Synchronous Layout:

    • Happens when JS queries layout (offsetHeight, getBoundingClientRect) right after DOM mutations.
    • Forces browser to flush pending layout tasks → expensive.

⚠️ Example:

// BAD: Forces reflow
el.style.width = "200px";
console.log(el.offsetHeight); // Forces sync layout

Best Practice:

  • Batch reads & writes using libraries like FastDOM.
  • Minimize DOM thrashing inside loops.

🔹 9. Practical Examples & Best Practices
  • Optimize Paint/Compositing:

    • Use will-change: transform, opacity;.
  • Reduce Layout Thrashing:

    // BAD
    for (let i=0; i<1000; i++) {
      el.style.left = i + "px";
      console.log(el.offsetWidth);
    }
    
    // GOOD
    const width = el.offsetWidth;
    for (let i=0; i<1000; i++) {
      el.style.left = i + "px";
    }
  • Script Loading:

    • Place scripts at bottom or use defer.
  • CSS:

    • Inline critical CSS, defer non-critical.
  • Animations:

    • Stick to transform and opacity.
  • Event Loop Optimization:

    • Break long tasks with setTimeout(fn, 0) or queueMicrotask.

📌 Key Takeaways

  • Rendering is pipeline-based (Parse → Style → Layout → Paint → Composite).
  • JS execution blocks rendering.
  • Avoid forced sync layouts.
  • Use rAF for animations & keep work under 16ms per frame.
  • Always optimize CRP for fast first render.
⚠️ **GitHub.com Fallback** ⚠️