Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
🛡️ Days 23–27 – Professional JavaScript  ·  Day 25

Day 25: DOM Performance & Patterns

⏱ 95 min📶 Pro🎯 100% self-study
Day 25 / 30

✅ Today you will learn

  • How the browser renders: JS → style → layout → paint
  • Layout thrashing and batching reads/writes
  • requestAnimationFrame, passive listeners, lazy-loading code
  • Measuring with performance.now, DevTools Performance and Lighthouse
  • Patterns: module, pub/sub (observer) and a tiny store

🏢 In the real world

On the owner’s ₹8,000 phone the product page takes 4 seconds to become usable and scrolling stutters. Google measures this (INP, LCP) and ranks slower sites lower; customers leave. Most JavaScript slowness comes from a handful of mistakes – today you learn to find them with measurements and fix them with proven patterns.

How the browser draws a frame

JavaScript runsStyle – which CSS appliesLayout – sizes and positions (expensive)Paint – pixelsComposite – layers on screen
Each frame has about 16 ms at 60 fps
🧠 The 50 ms rule

Any JavaScript task longer than 50 ms blocks taps and scrolling (“long task”). Keep event handlers short, move heavy work off the main path (chunks, Web Workers) and update the DOM in batches.

Layout thrashing: read, then write

HTML + JavaScript
<button id="slow">Slow: read/write mixed</button> <button id="fast">Fast: read all, then write all</button>
<p id="t"></p>
<div id="boxes"></div>

<script>
const boxes = document.querySelector('#boxes');
for (let i = 0; i < 400; i++) {
  const d = document.createElement('div');
  d.style.cssText = 'height:4px;margin:1px 0;background:#fde68a;width:50%';
  boxes.append(d);
}
const items = [...boxes.children];

document.querySelector('#slow').addEventListener('click', () => {
  const t0 = performance.now();
  for (const el of items) {
    const w = el.offsetWidth;          // READ (forces layout)
    el.style.width = (w % 300) + 20 + 'px';   // WRITE (invalidates layout) → next read recalculates
  }
  document.querySelector('#t').textContent = `Mixed: ${(performance.now() - t0).toFixed(1)} ms`;
});
document.querySelector('#fast').addEventListener('click', () => {
  const t0 = performance.now();
  const widths = items.map(el => el.offsetWidth);                  // all READS first
  items.forEach((el, i) => { el.style.width = (widths[i] % 300) + 20 + 'px'; });   // then all WRITES
  document.querySelector('#t').textContent = `Batched: ${(performance.now() - t0).toFixed(1)} ms`;
});
</script>
▶  Compare the timings – batching avoids hundreds of forced layouts
Triggers layout when read after a writePrefer
offsetWidth/Height, clientWidth, scrollTopRead once, cache the value
getBoundingClientRect()IntersectionObserver / ResizeObserver
getComputedStyle()Toggle classes instead of reading styles
Changing width/top in a looptransform and opacity (CSS Day 13)

requestAnimationFrame and passive listeners

HTML + JavaScript
<div id="bar" style="height:6px;background:#ca8a04;width:0;position:sticky;top:0;transition:none"></div>
<div id="page" style="height:240px;overflow:auto;border:1px solid #e5e7eb;border-radius:8px;padding:10px">
  <p>Scroll this article – the reading-progress bar above updates once per frame.</p>
  <div style="height:900px;background:linear-gradient(#fff,#fef3c7)"></div>
</div>

<script>
const page = document.querySelector('#page');
const bar = document.querySelector('#bar');
let ticking = false;

page.addEventListener('scroll', () => {
  if (ticking) return;                     // at most one update per frame
  ticking = true;
  requestAnimationFrame(() => {
    const max = page.scrollHeight - page.clientHeight;
    bar.style.width = (page.scrollTop / max * 100) + '%';
    ticking = false;
  });
}, { passive: true });                     // promise not to call preventDefault → smoother scrolling
</script>
▶  A reading-progress bar: scroll + rAF + passive listener

Long tasks: split work into chunks

JavaScript
// Processing 50,000 order rows in one go blocks the page; yield between chunks
const orders = Array.from({ length: 50000 }, (_, i) => ({ id: i, total: (i * 37) % 2500 }));
const yieldToBrowser = () => new Promise(r => setTimeout(r, 0));   // browsers also have scheduler.yield()

async function totalInChunks(rows, size = 5000) {
  let sum = 0, chunks = 0;
  for (let i = 0; i < rows.length; i += size) {
    for (const row of rows.slice(i, i + size)) sum += row.total;
    chunks++;
    await yieldToBrowser();                  // let clicks and paints happen between chunks
  }
  return { sum, chunks };
}
const t0 = performance.now();
totalInChunks(orders).then(r => console.log(`₹${r.sum.toLocaleString('en-IN')} in ${r.chunks} chunks, ${Math.round(performance.now() - t0)} ms total`));
₹6,24,75,000 in 10 chunks, 40 ms total
💡 Load heavy code only when needed

A chart library used only on the admin report page should not slow down the home page: btn.onclick = async () => { const { drawChart } = await import("./chart.js"); drawChart(); }. Vite splits it into a separate file automatically.

Measure before you optimise

ToolShows
performance.now() / console.timeHow long one function takes
DevTools → Performance → RecordLong tasks (red corners), layout, paint per frame
LighthouseLCP, INP, CLS, total blocking time + suggestions
DevTools → CoverageUnused JS and CSS bytes
Network tab (Slow 4G throttling)What a real Indian mobile user experiences

Patterns that keep big apps organised

JavaScript
// Pub/Sub (observer): parts of the app talk without knowing each other
function createEventBus() {
  const listeners = {};
  return {
    on(event, fn) { (listeners[event] ||= []).push(fn); return () => this.off(event, fn); },
    off(event, fn) { listeners[event] = (listeners[event] || []).filter(f => f !== fn); },
    emit(event, data) { (listeners[event] || []).forEach(fn => fn(data)); },
  };
}

// A tiny store: one state object, updates through actions, subscribers re-render
function createStore(initial, bus) {
  let state = initial;
  return {
    get: () => state,
    update(change) { state = { ...state, ...change(state) }; bus.emit('change', state); },
  };
}

const bus = createEventBus();
const store = createStore({ cart: [], total: 0 }, bus);
const stopBadge = bus.on('change', s => console.log(`🛒 badge: ${s.cart.length} item(s)`));
bus.on('change', s => console.log(`💰 summary: ₹${s.total}`));

store.update(s => ({ cart: [...s.cart, 'Kaju'], total: s.total + 900 }));
stopBadge();                                     // unsubscribe one listener
store.update(s => ({ cart: [...s.cart, 'Ladoo'], total: s.total + 500 }));
🛒 badge: 1 item(s) 💰 summary: ₹900 💰 summary: ₹1400
PatternSolvesSeen in
ModulePrivate details + small public APIES modules, every library
Observer / pub-subMany parts react to one changeaddEventListener, Redux subscribe, Vue reactivity
Store (single state)One source of truthRedux, Zustand, our to-do app
DelegationOne listener for many elementsLists, tables, menus
FactoryCreate configured objectscreateStore, createCounter

🏅 Job skills unlocked today

✔ Explain the rendering pipeline and long tasks✔ Avoid layout thrashing and use rAF / passive listeners✔ Split long work and lazy-load heavy code✔ Measure with DevTools and organise code with pub/sub and stores

Practice tasks

🏋️ Practice – do it yourself

Task 1: Build a “back to top” button that appears after scrolling 300px – use IntersectionObserver on a sentinel instead of a scroll listener.
Task 2: Use the event bus to update a cart badge and a toast when an item is added, from two independent modules.
Task 3: Record a Performance profile of your Day 14 to-do app while adding 50 tasks and find the longest task.

Quick quiz

🧠 Quick Quiz – test yourself

1. Layout thrashing is caused by…
Forced synchronous layout.
2. Visual updates on scroll should be scheduled with…
Once per frame.
3. { passive: true } on a scroll listener…
Browser can scroll immediately.
4. A task longer than … ms is a “long task”:
Blocks interaction.
5. Pub/sub lets modules…
Loose coupling.

Interview corner

💼 Interview Corner

How would you speed up a slow page?

Measure first (Lighthouse, Performance panel). Then reduce and defer JavaScript (code-split, lazy-load), avoid long tasks (chunk work, Web Workers), batch DOM reads/writes, use rAF and passive listeners, optimise images/fonts, cache API data and re-measure Core Web Vitals.

What is reflow (layout) and how do you avoid unnecessary ones?

Reflow is the browser recalculating element geometry. It happens after DOM/style changes and is forced synchronously when you read layout properties after writing. Avoid by batching reads before writes, changing classes instead of many inline styles, animating transform/opacity and using observers instead of polling positions.

Summary

  • Frames have ~16 ms; tasks over 50 ms block interaction.
  • Batch DOM reads before writes; use rAF and passive listeners.
  • Chunk long work, lazy-load heavy modules, measure with DevTools.
  • Pub/sub and a single store keep growing apps organised.
➡️ Next

Day 26: security and clean code – XSS, safe rendering, secrets, CSP and writing code your team will thank you for.

Frequently Asked Questions

What are Web Workers?
Background threads that run JavaScript without blocking the page – ideal for heavy calculations like parsing large CSV reports. They cannot touch the DOM directly and talk with postMessage.
Is vanilla JS faster than React?
Well-written vanilla code can be very fast; frameworks add some cost but give structure and efficient updates. Most slowness comes from app code, not the framework choice.
← JavaScript (30 Days)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicDay 26: Security & Clean Code Your progress: 25 / 30 (83%) · Lesson 26 / 30 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free