jQuery Best Practices

This guide collects patterns used by professionals to write fast, accessible, and maintainable jQuery. Each topic includes an explanation, short example, and a Try it Yourself » demo.

Quick Reference

Practice Why it matters Example
Cache selectors Reduces repeated DOM queries; speeds up loops and handlers. var $list = $('#list'); $list.append(...)
Event delegation One handler works for current and future elements; lower memory. $('#list').on('click', 'li.item', fn)
Namespace handlers Easy to unbind specific features without breaking others. $('#box').on('click.demo', fn); $('#box').off('click.demo')
Throttle / debounce Prevents excessive work on scroll/resize/input. $(win).on('scroll', throttle(fn, 100))
Batch DOM updates Minimizes layout thrash; big gains with large inserts. Build HTML string/fragment and append once.
detach() & reinsert Do heavy mutations off-DOM, then insert once. var $tmp = $node.detach(); ... $('body').append($tmp)
Accessibility (ARIA) Screen readers & keyboard users get feedback and focus. <div role="status" aria-live="polite">
Feature detect Avoid brittle UA sniffing; adapt by capabilities. if (window.fetch) { ... }
Promise error handling Show user-friendly errors; avoid silent failures. $.get(...).fail(xhr => show(xhr.status))

1) Cache jQuery selections

Query the DOM once and reuse the jQuery object. Build strings/fragments and append once instead of many small append() calls.


var $list = $('#list');
var html = [];
for(var i=0; i<200; i++){ html.push('<li>'+i+'</li>'); }
$list.append(html.join(''));

2) Use event delegation for dynamic content

Attach one handler on a stable parent; match children by selector. Works for elements added later.


$('#list').on('click', 'li.item', function(){ /* ... */ });

3) Namespace your handlers & cleanly unbind

Namespaced events avoid conflicts and allow safe teardown.


$('#box').on('click.demoNS', function(){ ... });
$('#box').off('click.demoNS');

4) Throttle and debounce high-frequency events

Throttle runs at most every N ms; debounce waits for quiet time. Great for scroll/resize and live search.


function throttle(fn, wait){ /* ... */ }
function debounce(fn, wait){ /* ... */ }
$(window).on('scroll', throttle(fn, 100));
$('#search').on('input', debounce(fn, 300));

5) Batch DOM updates

Build strings or use a DocumentFragment; append once. This avoids repeated style/layout recalculations.


var html = [];
for(var i=0; i<500; i++){ html.push('<div class="card">'+i+'</div>'); }
$('#wrap').append(html.join(''));

6) Do heavy work with detach() then reinsert

Mutate large subtrees off-DOM for speed; reinsert once.


var $tmp = $('#big').detach();
// mutate $tmp here...
$('body').append($tmp);

7) Accessibility: ARIA roles & focus management

Announce changes via live regions and keep logical focus order for keyboard users.


<div id="toast" role="status" aria-live="polite"></div>

8) Prefer feature detection over UA sniffing

Browsers vary; detect capabilities, not names.


if(window.fetch){ // use fetch, else use $.ajax fallback }

9) Promise-based error handling

Surface friendly errors; log details for debugging.


$.get('/api').done(function(data){ ... }).fail(function(xhr){ /* show xhr.status */ });
Production checklist: 1) cache selectors, 2) use delegation, 3) namespace events, 4) throttle/ debounce, 5) batch DOM or use virtual DOM, 6) clean up on page changes, 7) add ARIA + keyboard support, 8) feature detect, 9) handle errors and timeouts gracefully.