jQuery Performance

Cache selectors, delegate events, and batch DOM writes to keep pages fast.

jQuery is rarely the bottleneck — how you use it is. A handful of habits keep even a large, jQuery-heavy page responsive.

1. Cache your selections

Every $(".thing") re-scans the DOM. If you use a set more than once, store it:

// Wasteful — three DOM scans
$("#panel").addClass("open");
$("#panel").fadeIn();
$("#panel").css("color", "blue");

// Cached — one scan, then chain
var $panel = $("#panel");
$panel.addClass("open").fadeIn().css("color", "blue");

The $name convention (a leading $ on the variable) signals “this holds a jQuery object.”

2. Delegate instead of binding hundreds of handlers

One listener on a parent beats a thousand on the children — less memory, and it covers elements added later.

3. Batch DOM writes

Touching the DOM in a loop forces repeated layout work. Build the markup as a string (or a detached fragment) and insert once:

// Slow — 100 separate insertions / reflows
for (var i = 0; i < 100; i++) { $("#list").append("<li>" + i + "</li>"); }

// Fast — build once, insert once
var html = "";
for (var i = 0; i < 100; i++) { html += "<li>" + i + "</li>"; }
$("#list").append(html);

4. Prefer specific selectors and scope

ID lookups are fastest. When searching within a known region, scope the search: $panel.find("li") beats a document-wide $("#panel li") re-scan.

Example

Try it yourself
Loading editor…
Press Run to see the result.

When to use it

  • A large data table caches $("#grid tbody") once on load so that scroll handlers do not re-query the DOM thousands of times per second.
  • A chat app batches 50 new message elements into a single DocumentFragment and appends once to avoid 50 reflows.
  • A search filter uses .detach() to remove the list from the DOM, filters all items, then reattaches to trigger only one repaint.

More examples

Cache selectors outside event handlers

Storing selections in variables avoids running the CSS selector engine on every handler invocation.

Example · html
<ul id="list">
  <li>A</li><li>B</li><li>C</li>
</ul>
<button id="go">Process</button>
<script>
$(function () {
  var $list  = $("#list");       // queried once
  var $items = $list.find("li"); // queried once
  $("#go").click(function () {
    $items.css("color", "steelblue"); // reuses cached set
  });
});
</script>

Batch DOM inserts with a fragment

Building all elements inside a DocumentFragment and appending once causes only one reflow instead of 100.

Example · html
<ul id="feed"></ul>
<button id="load">Load 100 items</button>
<script>
$(function () {
  $("#load").click(function () {
    var $frag = $(document.createDocumentFragment());
    for (var i = 1; i <= 100; i++) {
      $frag.append($("<li>Item " + i + "</li>"));
    }
    $("#feed").append($frag);
  });
});
</script>

Use .detach() for heavy DOM rewrites

.detach() removes rows from the live DOM before sorting and reattaches them, reducing layout recalculations to one.

Example · html
<table id="bigTable"><tbody></tbody></table>
<button id="sort">Sort rows</button>
<script>
$(function () {
  // pre-populate
  for (var i = 5; i >= 1; i--) {
    $("#bigTable tbody").append("<tr data-val='" + i + "'><td>Row " + i + "</td></tr>");
  }
  $("#sort").click(function () {
    var $tbody = $("#bigTable tbody");
    var $rows  = $tbody.find("tr").detach();
    $rows.sort(function (a, b) {
      return $(a).data("val") - $(b).data("val");
    });
    $tbody.append($rows);
  });
});
</script>

Discussion

  • Be the first to comment on this lesson.