name: jquery description: "[Applies to: **/*.{js,jsx}] Definitive guidelines for writing performant, maintainable, and modern jQuery code, emphasizing best practices for DOM manipulation, event handling, and compatibility with jQuery 4.0 and ES6+." source: "cursor_mdc"
jQuery Best Practices
jQuery remains a powerful tool for DOM manipulation, but its use in 2025 demands a performance-first mindset and adherence to modern JavaScript standards. This guide outlines the definitive best practices for writing fast, clean, and maintainable jQuery code.
1. Code Organization and Modern JavaScript
Always encapsulate your jQuery code within an Immediately Invoked Function Expression (IIFE) to prevent global scope pollution and ensure $ correctly maps to jQuery, especially in environments like WordPress where jQuery.noConflict() is active. Embrace modern ES6+ syntax where possible.
1.1 Scope and $.noConflict()
Always wrap your jQuery code in an IIFE. This ensures $ refers to jQuery and protects your code from global scope conflicts.
❌ BAD: Global scope pollution, $ conflicts
// Potentially conflicts with other libraries or scripts
$('.my-element').on('click', function() {
console.log('Clicked!');
});
✅ GOOD: Isolated scope, $ aliased correctly
(function($) {
// Your jQuery code here, $ is safely aliased to jQuery
$('.my-element').on('click', function() {
console.log('Clicked!');
});
})(jQuery); // Pass jQuery object into the IIFE
1.2 Modern JavaScript Syntax
Integrate const, let, arrow functions, and template literals. jQuery code coexists perfectly with modern JS.
❌ BAD: Old-school var and function syntax
var myElement = $('#old-element');
myElement.click(function() {
var message = 'Element ' + myElement.attr('id') + ' clicked.';
console.log(message);
});
✅ GOOD: Modern ES6+ syntax
const myElement = $('#modern-element');
myElement.on('click', () => {
const message = `Element ${myElement.attr('id')} clicked.`;
console.log(message);
});
2. Performance Considerations
Prioritize performance by minimizing DOM interactions, optimizing selectors, and caching frequently used elements and values.
2.1 Cache Selectors
Never re-query the DOM for the same element. Store jQuery objects in variables for reuse. Prefix jQuery variables with $ for clarity.
❌ BAD: Repeated DOM queries
$('.item').addClass('active');
$('.item').css('background-color', 'blue');
$('.item').on('click', function() { /* ... */ });
✅ GOOD: Cache selectors for efficiency
const $items = $('.item');
$items.addClass('active');
$items.css('background-color', 'blue');
$items.on('click', function() { /* ... */ });
2.2 Minimize DOM Updates
Batch DOM modifications. Build HTML fragments off-document or use detach() before making multiple changes, then re-attach once. Each DOM write triggers potential reflows.
❌ BAD: Repeated DOM appends in a loop
const data = ['one', 'two', 'three'];
const $list = $('#my-list');
data.forEach(item => {
$list.append(`<li>${item}</li>`); // Each append causes a DOM update
});
✅ GOOD: Batch DOM updates
const data = ['one', 'two', 'three'];
const $list = $('#my-list');
const $fragment = $(document.createDocumentFragment()); // Create a document fragment
data.forEach(item => {
$fragment.append(`<li>${item}</li>`);
});
$list.append($fragment); // Append once
2.3 Optimize Selectors
Use the most specific and efficient selectors possible. Prefer ID and class selectors over complex descendant or attribute selectors.
❌ BAD: Inefficient descendant selector
// Sizzle has to traverse all divs, then check for 'my-class'
$('div .my-class').css('color', 'red');
✅ GOOD: Direct class selector
// Direct and fast
$('.my-class').css('color', 'red');
2.4 Cache Loop Lengths
When iterating over jQuery collections or arrays, cache the length to avoid re-evaluating it on each iteration.
❌ BAD: Re-evaluating length in each loop iteration
const $elements = $('.my-elements');
for (let i = 0; i < $elements.length; i++) {
$elements.eq(i).addClass('processed');
}
✅ GOOD: Cache loop length
const $elements = $('.my-elements');
const numElements = $elements.length; // Cache length
for (let i = 0; i < numElements; i++) {
$elements.eq(i).addClass('processed');
}
3. Common Patterns and Anti-patterns
Adopt patterns that promote maintainability and performance.
3.1 Delegated Events
Always use event delegation for dynamic content or large collections of elements. Attach a single event handler to a static parent element (or document) and use the selector argument of .on().
❌ BAD: Attaching many individual event handlers
// Attaches a handler to *every* current button. New buttons won't work.
$('button.delete').on('click', function() {
$(this).closest('.item').remove();
});
✅ GOOD: Event delegation
// Attaches one handler to the document, efficient for dynamic content
$(document).on('click', 'button.delete', function() {
$(this).closest('.item').remove();
});
3.2 Element-Specific State with $.data()
Use $.data() to store and retrieve arbitrary data associated with DOM elements. This is cleaner and more memory-safe than attaching custom properties directly to DOM nodes.
❌ BAD: Attaching custom properties directly
const $el = $('#my-element');
$el[0].customState = { count: 0 }; // Pollutes DOM node
$el.on('click', function() {
$el[0].customState.count++;
console.log($el[0].customState.count);
});
✅ GOOD: Using $.data()
const $el = $('#my-element');
$el.data('customState', { count: 0 }); // Cleanly stores data
$el.on('click', function() {
const state = $el.data('customState');
state.count++;
$el.data('customState', state); // Update data
console.log($el.data('customState').count);
});
4. Common Pitfalls and Gotchas
Be aware of jQuery's nuances and recent changes, especially with jQuery 4.0.
4.1 $(document).ready() Syntax
As of jQuery 3.0, only $(handler) is the recommended syntax for DOM ready. Avoid deprecated forms.
❌ BAD: Deprecated or misleading ready syntaxes
$(document).ready(function() { /* ... */ }); // Deprecated
$('img').ready(function() { /* ... */ }); // Misleading, doesn't wait for images
$(document).on('ready', function() { /* ... */ }); // Removed in jQuery 3.0
✅ GOOD: Recommended ready syntax
$(function() {
// Code executes when the DOM is fully loaded.
console.log('DOM is ready!');
});
4.2 jQuery 4.0 API Changes
Stay updated with the jQuery 4.0 upgrade guide. Key breaking changes include toggleClass(Boolean|undefined) removal and updated Ajax API.
❌ BAD: Using toggleClass(Boolean) (removed in 4.0)
// This will break in jQuery 4.0
$('.my-element').toggleClass('active', true);
✅ GOOD: Use addClass() or removeClass() explicitly
const isActive = true; // Or some condition
if (isActive) {
$('.my-element').addClass('active');
} else {
$('.my-element').removeClass('active');
}
// Or for toggling based on existence:
$('.my-element').toggleClass('active');
❌ BAD: Relying on auto-execution of scripts in Ajax (removed in 4.0)
// If fetching a script, this will no longer auto-execute in jQuery 4.0
$.ajax({
url: '/my-script.js'
});
✅ GOOD: Explicitly define dataType: "script" for script execution
$.ajax({
url: '/my-script.js',
dataType: 'script' // Explicitly tells jQuery to execute the script
});
5. Accessibility
While jQuery itself doesn't directly provide accessibility features, ensure your DOM manipulations respect ARIA attributes and semantic HTML.
❌ BAD: Relying solely on visual changes for state
$('#my-button').on('click', function() {
$(this).css('opacity', '0.5'); // Visually disabled, but not for screen readers
});
✅ GOOD: Updating ARIA attributes for accessibility
$('#my-button').on('click', function() {
$(this).attr('aria-disabled', 'true').prop('disabled', true).addClass('disabled-style');
});
6. WordPress Specifics
If working within a WordPress environment, always use the bundled jQuery version and respect its noConflict mode.
❌ BAD: Bundling your own jQuery version in a WordPress theme
<script src="/path/to/my/jquery-3.7.1.min.js"></script>
✅ GOOD: Enqueue WordPress's bundled jQuery
// In functions.php
wp_enqueue_script('my-script', get_template_directory_uri() . '/js/my-script.js', array('jquery'), '1.0.0', true);
Then, use the IIFE wrapper as described in 1.1 for your my-script.js file.
// my-script.js
(function($) {
// Your jQuery code here, $ is safely aliased to jQuery
$('#wp-element').on('click', function() {
console.log('WordPress element clicked!');
});
})(jQuery);