Most slow pages are not slow because JavaScript is slow. They are slow because the code does too much work, or does it at the wrong time. The cheapest operation is the one you do not do.
1. Do not repeat the same lookup
// Searches the document on every loop
for (let i = 0; i < 1000; i++) {
document.querySelector("#list").append(makeItem(i));
}
// Look it up once
const list = document.querySelector("#list");
for (let i = 0; i < 1000; i++) {
list.append(makeItem(i));
}
2. Batch DOM changes
Build in memory, insert once. Details are in DOM Performance Basics.
<ul id="out"></ul>
<script>
const out = document.querySelector("#out");
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 200; i++) {
const li = document.createElement("li");
li.textContent = "Item " + i;
fragment.append(li);
}
out.append(fragment); // one change to the live page
console.log(out.children.length); // → 200
</script>
3. Use event delegation
One listener on a container instead of hundreds on items: Event Delegation.
4. Do not redo work that has not changed
If nothing changed, do not re-render. A cheap check saves a lot:
let lastTotal = null;
function renderTotal(total) {
if (total === lastTotal) return; // nothing to do
lastTotal = total;
console.log("rendering", total);
}
renderTotal(10); // → rendering 10
renderTotal(10); // skipped
renderTotal(12); // → rendering 12
5. Stop work nobody can see
- Cancel timers and intervals when a component is removed (
clearInterval). - Remove event listeners you no longer need (
removeEventListener, or thesignaloption). - Disconnect observers (
observer.disconnect()). - Pause animations and polling when the tab is hidden (
document.hidden,visibilitychange).
let timer;
function start() { timer = setInterval(refresh, 5000); }
function stop() { clearInterval(timer); }
document.addEventListener("visibilitychange", () => {
document.hidden ? stop() : start();
});
6. Choose the right data structure
A Set or Map lookup is fast. Searching a long array with includes() inside a loop is slow, since it scans every time.
const ids = Array.from({ length: 1000 }, (_, i) => i);
const lookup = new Set(ids);
console.log(lookup.has(999)); // → true (fast, however large the set)
7. Prefer CSS where possible
Hover effects, transitions and animations are smoother in CSS than in JavaScript timers, and do not use the main thread.
8. Do less per event
Events like scroll, mousemove and input can fire many times a second. Keep handlers light, and use debounce or throttle for heavy work.
9. Keep data small
Do not fetch or render 5,000 rows when the user sees 20. Paginate, filter on the server, or render only the visible rows.
What does not matter much
Micro-optimisations such as for versus forEach, or ++i versus i++, almost never change anything you can notice. Fix the big costs first, and measure to find them (Measuring Performance).