JavaScript Performance Optimization

matt
Matthew Gros · Nov 3, 2025

TLDR

Minimize DOM manipulation, debounce events, lazy load resources, profile before optimizing.

JavaScript Performance Optimization

Fast JavaScript = Happy Users

Performance matters more than you think.

DOM Manipulation

// Bad - multiple reflows
for (let i = 0; i < 1000; i++) {
    container.innerHTML += `<div>${i}</div>`
}

// Good - single reflow
const html = Array.from({length: 1000}, (_, i) => `<div>${i}</div>`).join('')
container.innerHTML = html

// Better - document fragment
const fragment = document.createDocumentFragment()
for (let i = 0; i < 1000; i++) {
    const div = document.createElement('div')
    div.textContent = i
    fragment.appendChild(div)
}
container.appendChild(fragment)

Event Handling

// Bad - handler on every item
items.forEach(item => {
    item.addEventListener('click', handleClick)
})

// Good - event delegation
container.addEventListener('click', (e) => {
    if (e.target.matches('.item')) {
        handleClick(e)
    }
})

Debounce and Throttle

// Debounce - wait until user stops
function debounce(fn, delay) {
    let timeout
    return (...args) => {
        clearTimeout(timeout)
        timeout = setTimeout(() => fn(...args), delay)
    }
}

// Usage: search input
input.addEventListener('input', debounce(search, 300))

// Throttle - limit frequency
function throttle(fn, limit) {
    let waiting = false
    return (...args) => {
        if (!waiting) {
            fn(...args)
            waiting = true
            setTimeout(() => waiting = false, limit)
        }
    }
}

// Usage: scroll handler
window.addEventListener('scroll', throttle(handleScroll, 100))

Lazy Loading

// Images
<img loading="lazy" src="image.jpg">

// Code splitting (dynamic imports)
button.addEventListener('click', async () => {
    const { heavyFunction } = await import('./heavy-module.js')
    heavyFunction()
})

Measure First

console.time('operation')
doSomething()
console.timeEnd('operation')

// Or Performance API
performance.mark('start')
doSomething()
performance.mark('end')
performance.measure('operation', 'start', 'end')

Use DevTools Performance tab for real profiling.

About the Author

matt

I build and ship automation-driven products using Laravel and modern frontend stacks (Vue/React), with a focus on scalability, measurable outcomes, and tight user experience. I’m based in Toronto, have 13+ years in PHP, and I also hold a pilot’s license. I enjoy working on new tech projects and generally exploring new technology.