💲 Working with jQuery in WordPress
Master jQuery integration in WordPress themes
Learn no-conflict mode, best practices, and common jQuery patterns
Learning Objectives
- Understand jQuery in WordPress context
- Master jQuery no-conflict mode
- Use WordPress's bundled jQuery properly
- Create jQuery plugins for WordPress
- Handle jQuery events and DOM manipulation
- Implement jQuery AJAX in WordPress
- Migrate from jQuery to vanilla JavaScript
- Debug jQuery issues in WordPress
jQuery in WordPress
WordPress includes jQuery by default and many themes and plugins depend on it. Understanding how to work with jQuery in WordPress's no-conflict mode is essential for theme development.
Key Concept
WordPress jQuery Version
WordPress includes jQuery but may not always have the latest version due to compatibility concerns. As of WordPress 5.9+:
- jQuery 3.6.0+ is included
- jQuery Migrate is included for backward compatibility
- jQuery UI components are available
- No-conflict mode is enabled by default
Understanding No-Conflict Mode
Why No-Conflict Mode?
// This will NOT work in WordPress by default
$(document).ready(function() {
$('.button').click(function() {
// $ is undefined
});
});
// This WILL work
jQuery(document).ready(function() {
jQuery('.button').click(function() {
// jQuery is available
});
});
Common No-Conflict Patterns
// Pattern 1: IIFE with $ parameter
(function($) {
'use strict';
// $ is available inside this function
$(document).ready(function() {
$('.button').click(function() {
console.log('Button clicked!');
});
});
})(jQuery);
// Pattern 2: jQuery ready with $ parameter
jQuery(document).ready(function($) {
// $ is available inside this function
$('.button').click(function() {
console.log('Button clicked!');
});
});
// Pattern 3: Shorter syntax
jQuery(function($) {
// $ is available inside this function
$('.button').click(function() {
console.log('Button clicked!');
});
});
// Pattern 4: Using const/let
const $ = jQuery.noConflict();
$(document).ready(function() {
// $ is now available
$('.button').click(function() {
console.log('Button clicked!');
});
});
Properly Enqueuing jQuery
Using WordPress's jQuery
<?php
// functions.php
// Correct way - Use WordPress's jQuery
function mytheme_enqueue_scripts() {
// jQuery is included as a dependency
wp_enqueue_script(
'mytheme-script',
get_template_directory_uri() . '/assets/js/main.js',
array('jquery'), // jQuery dependency
'1.0.0',
true
);
}
add_action('wp_enqueue_scripts', 'mytheme_enqueue_scripts');
// Using specific jQuery UI components
function mytheme_enqueue_jquery_ui() {
// Enqueue jQuery UI components
wp_enqueue_script('jquery-ui-core');
wp_enqueue_script('jquery-ui-datepicker');
wp_enqueue_script('jquery-ui-accordion');
wp_enqueue_script('jquery-ui-tabs');
wp_enqueue_script('jquery-ui-sortable');
// Enqueue jQuery UI CSS
wp_enqueue_style(
'jquery-ui',
'https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css',
array(),
'1.12.1'
);
}
add_action('wp_enqueue_scripts', 'mytheme_enqueue_jquery_ui');
// Replace WordPress jQuery with custom version (NOT RECOMMENDED)
function mytheme_replace_jquery() {
if (!is_admin()) {
wp_deregister_script('jquery');
wp_register_script(
'jquery',
'https://code.jquery.com/jquery-3.6.0.min.js',
array(),
'3.6.0',
true
);
wp_enqueue_script('jquery');
}
}
// add_action('wp_enqueue_scripts', 'mytheme_replace_jquery'); // Uncomment only if necessary
Replacing WordPress's jQuery with a custom version can break plugins and themes that depend on the bundled version. Only do this if absolutely necessary and test thoroughly.
jQuery DOM Manipulation in WordPress
Common jQuery Operations
(function($) {
'use strict';
$(document).ready(function() {
// Selecting elements
const $header = $('.site-header');
const $navigation = $('#site-navigation');
const $buttons = $('button.primary');
// Class manipulation
$header.addClass('is-sticky');
$navigation.removeClass('hidden');
$buttons.toggleClass('active');
// Attribute manipulation
$('img').attr('loading', 'lazy');
$('a[target="_blank"]').attr('rel', 'noopener noreferrer');
$('.form-input').prop('disabled', false);
// Content manipulation
$('.site-title').text('New Title');
$('.content').html('<p>New content</p>');
$('.input').val('New value');
// DOM traversal
$('.menu-item').parent().addClass('has-children');
$('.widget').children('h3').addClass('widget-title');
$('.current').siblings().removeClass('active');
$('.post').find('.entry-title').addClass('highlighted');
// Creating elements
const $newButton = $('<button>', {
class: 'btn btn-primary',
text: 'Click me',
click: function() {
alert('Button clicked!');
}
});
// Inserting elements
$('.content').append($newButton);
$('.header').prepend('<div class="announcement">Welcome!</div>');
$('.sidebar').after('<aside class="secondary"></aside>');
$('.footer').before('<section class="cta"></section>');
// Removing elements
$('.temporary').remove();
$('.content').empty();
$('.wrapper').unwrap();
// Chaining
$('.post')
.find('.entry-content')
.addClass('formatted')
.find('img')
.wrap('<figure class="image-wrapper"></figure>')
.parent()
.append('<figcaption>Image caption</figcaption>');
});
})(jQuery);
jQuery Event Handling
Event Binding and Delegation
(function($) {
'use strict';
$(document).ready(function() {
// Direct event binding
$('.button').click(function(e) {
e.preventDefault();
console.log('Button clicked');
});
// Multiple events
$('.input').on('focus blur', function(e) {
$(this).toggleClass('active');
});
// Event delegation (for dynamic content)
$(document).on('click', '.dynamic-button', function(e) {
e.preventDefault();
console.log('Dynamic button clicked');
});
// Custom events
$('.widget').on('widget:refresh', function() {
$(this).find('.content').load('/api/widget-content');
});
// Trigger custom event
$('.widget').trigger('widget:refresh');
// Common WordPress events
// Mobile menu toggle
$('.menu-toggle').on('click', function() {
const $menu = $('.nav-menu');
const isOpen = $menu.hasClass('is-open');
$menu.toggleClass('is-open');
$(this).attr('aria-expanded', !isOpen);
// Trigger custom event
$(document).trigger('menu:toggled', [!isOpen]);
});
// Smooth scrolling
$('a[href^="#"]').on('click', function(e) {
const hash = this.hash;
if (hash) {
e.preventDefault();
$('html, body').animate({
scrollTop: $(hash).offset().top - 100
}, 800, function() {
window.location.hash = hash;
});
}
});
// Form validation
$('#contact-form').on('submit', function(e) {
const $form = $(this);
const $errors = $form.find('.error');
// Clear previous errors
$errors.removeClass('error');
// Validate required fields
$form.find('[required]').each(function() {
if (!$(this).val()) {
$(this).addClass('error');
e.preventDefault();
}
});
// Validate email
const $email = $form.find('input[type="email"]');
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if ($email.length && !emailRegex.test($email.val())) {
$email.addClass('error');
e.preventDefault();
}
});
// Infinite scroll
let loading = false;
$(window).on('scroll', function() {
if (loading) return;
const scrollHeight = $(document).height();
const scrollPosition = $(window).height() + $(window).scrollTop();
if ((scrollHeight - scrollPosition) < 200) {
loading = true;
loadMorePosts();
}
});
// Debounced resize
let resizeTimer;
$(window).on('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
// Handle resize
adjustLayout();
}, 250);
});
// Keyboard navigation
$(document).on('keydown', function(e) {
// Escape key
if (e.keyCode === 27) {
$('.modal').removeClass('is-open');
$('.dropdown').removeClass('is-open');
}
// Arrow keys for gallery
if ($('.gallery').hasClass('is-open')) {
if (e.keyCode === 37) { // Left arrow
navigateGallery('prev');
} else if (e.keyCode === 39) { // Right arrow
navigateGallery('next');
}
}
});
});
})(jQuery);
jQuery AJAX in WordPress
Complete jQuery AJAX Implementation
(function($) {
'use strict';
$(document).ready(function() {
// Load more posts with AJAX
$('#load-more').on('click', function() {
const $button = $(this);
const page = $button.data('page') || 1;
const category = $button.data('category');
// Disable button and show loading
$button.prop('disabled', true).text('Loading...');
$.ajax({
url: wp_ajax.ajax_url,
type: 'POST',
dataType: 'json',
data: {
action: 'load_more_posts',
nonce: wp_ajax.nonce,
page: page + 1,
category: category
},
success: function(response) {
if (response.success) {
// Append new posts
$('#posts-container').append(response.data.html);
// Update page number
$button.data('page', page + 1);
// Check if more posts available
if (page + 1 >= response.data.max_pages) {
$button.hide();
} else {
$button.prop('disabled', false).text('Load More');
}
// Trigger event for other scripts
$(document).trigger('posts:loaded', [response.data]);
} else {
alert('Error: ' + response.data);
}
},
error: function(xhr, status, error) {
console.error('AJAX Error:', error);
$button.prop('disabled', false).text('Try Again');
},
complete: function() {
// Always executed
}
});
});
// Like post functionality
$('.like-button').on('click', function() {
const $button = $(this);
const postId = $button.data('post-id');
const $count = $button.find('.like-count');
// Optimistic UI update
$button.toggleClass('liked');
$.post(wp_ajax.ajax_url, {
action: 'like_post',
nonce: wp_ajax.nonce,
post_id: postId
})
.done(function(response) {
if (response.success) {
$count.text(response.data.likes);
} else {
// Revert on error
$button.toggleClass('liked');
alert(response.data.message);
}
})
.fail(function() {
// Revert on error
$button.toggleClass('liked');
alert('An error occurred');
});
});
// Live search
let searchTimer;
$('#search-input').on('keyup', function() {
const query = $(this).val();
const $results = $('#search-results');
// Clear previous timer
clearTimeout(searchTimer);
if (query.length < 3) {
$results.empty().hide();
return;
}
// Debounce search
searchTimer = setTimeout(function() {
$.ajax({
url: wp_ajax.ajax_url,
type: 'GET',
data: {
action: 'live_search',
nonce: wp_ajax.nonce,
query: query
},
beforeSend: function() {
$results.html('<div class="loading">Searching...</div>').show();
},
success: function(response) {
if (response.success && response.data.results.length > 0) {
let html = '<ul class="search-results-list">';
$.each(response.data.results, function(index, item) {
html += `<li>
<a href="${item.url}">
<h4>${item.title}</h4>
<p>${item.excerpt}</p>
</a>
</li>`;
});
html += '</ul>';
$results.html(html);
} else {
$results.html('<p>No results found</p>');
}
},
error: function() {
$results.html('<p>Search error</p>');
}
});
}, 300);
});
// File upload with progress
$('#file-upload').on('change', function() {
const file = this.files[0];
if (!file) return;
const formData = new FormData();
formData.append('action', 'upload_file');
formData.append('nonce', wp_ajax.nonce);
formData.append('file', file);
$.ajax({
url: wp_ajax.ajax_url,
type: 'POST',
data: formData,
processData: false,
contentType: false,
xhr: function() {
const xhr = new XMLHttpRequest();
// Upload progress
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
$('#upload-progress').css('width', percentComplete + '%');
}
}, false);
return xhr;
},
success: function(response) {
if (response.success) {
$('#upload-result').html(
`<img src="${response.data.url}" alt="Uploaded">`
);
}
},
error: function() {
alert('Upload failed');
}
});
});
});
})(jQuery);
Creating jQuery Plugins for WordPress
jQuery Plugin Pattern
(function($) {
'use strict';
// Plugin definition
$.fn.wpAccordion = function(options) {
// Default settings
const settings = $.extend({
speed: 300,
closeOthers: true,
activeClass: 'is-active',
contentClass: 'accordion-content',
triggerClass: 'accordion-trigger',
startClosed: true,
callback: null
}, options);
// Plugin logic for each element
return this.each(function() {
const $accordion = $(this);
const $triggers = $accordion.find('.' + settings.triggerClass);
const $contents = $accordion.find('.' + settings.contentClass);
// Initialize
if (settings.startClosed) {
$contents.hide();
} else {
$contents.first().show();
$triggers.first().addClass(settings.activeClass);
}
// Bind events
$triggers.on('click', function(e) {
e.preventDefault();
const $trigger = $(this);
const $content = $trigger.next('.' + settings.contentClass);
const isOpen = $trigger.hasClass(settings.activeClass);
if (settings.closeOthers && !isOpen) {
$triggers.removeClass(settings.activeClass);
$contents.slideUp(settings.speed);
}
$trigger.toggleClass(settings.activeClass);
$content.slideToggle(settings.speed, function() {
// Callback after animation
if (typeof settings.callback === 'function') {
settings.callback.call(this, $trigger, $content);
}
});
});
// Public methods
$accordion.data('wpAccordion', {
open: function(index) {
$triggers.eq(index).trigger('click');
},
close: function(index) {
const $trigger = $triggers.eq(index);
if ($trigger.hasClass(settings.activeClass)) {
$trigger.trigger('click');
}
},
closeAll: function() {
$triggers.removeClass(settings.activeClass);
$contents.slideUp(settings.speed);
},
openAll: function() {
$triggers.addClass(settings.activeClass);
$contents.slideDown(settings.speed);
},
destroy: function() {
$triggers.off('click');
$accordion.removeData('wpAccordion');
}
});
});
};
// Auto-initialize
$(document).ready(function() {
$('.wp-accordion').wpAccordion();
});
// Another plugin example: Tabs
$.fn.wpTabs = function(options) {
const settings = $.extend({
activeClass: 'is-active',
animation: 'fade', // fade, slide, none
speed: 300,
responsive: true,
breakpoint: 768
}, options);
return this.each(function() {
const $tabs = $(this);
const $nav = $tabs.find('.tabs-nav');
const $navItems = $nav.find('a');
const $panels = $tabs.find('.tab-panel');
// Initialize
$panels.hide();
$panels.first().show();
$navItems.first().addClass(settings.activeClass);
// Tab switching
$navItems.on('click', function(e) {
e.preventDefault();
const $link = $(this);
const target = $link.attr('href');
const $target = $(target);
if ($link.hasClass(settings.activeClass)) return;
$navItems.removeClass(settings.activeClass);
$link.addClass(settings.activeClass);
if (settings.animation === 'fade') {
$panels.fadeOut(settings.speed);
$target.fadeIn(settings.speed);
} else if (settings.animation === 'slide') {
$panels.slideUp(settings.speed);
$target.slideDown(settings.speed);
} else {
$panels.hide();
$target.show();
}
});
// Responsive behavior
if (settings.responsive) {
$(window).on('resize', function() {
if ($(window).width() <= settings.breakpoint) {
$tabs.addClass('tabs-accordion');
} else {
$tabs.removeClass('tabs-accordion');
}
}).trigger('resize');
}
});
};
})(jQuery);
Using the Plugins
// Initialize with options
$('.my-accordion').wpAccordion({
speed: 500,
closeOthers: false,
callback: function($trigger, $content) {
console.log('Accordion toggled');
}
});
// Access plugin methods
const accordion = $('.my-accordion').data('wpAccordion');
accordion.open(2); // Open third item
accordion.closeAll(); // Close all items
// Initialize tabs
$('.my-tabs').wpTabs({
animation: 'slide',
speed: 400,
breakpoint: 640
});
Migrating from jQuery to Vanilla JavaScript
| jQuery | Vanilla JavaScript |
|---|---|
$(selector) |
document.querySelector(selector) |
$(selector).each() |
document.querySelectorAll(selector).forEach() |
$(el).addClass('class') |
el.classList.add('class') |
$(el).removeClass('class') |
el.classList.remove('class') |
$(el).toggleClass('class') |
el.classList.toggle('class') |
$(el).hasClass('class') |
el.classList.contains('class') |
$(el).attr('attr', 'value') |
el.setAttribute('attr', 'value') |
$(el).text('text') |
el.textContent = 'text' |
$(el).html('html') |
el.innerHTML = 'html' |
$(el).on('click', fn) |
el.addEventListener('click', fn) |
$(el).show() |
el.style.display = 'block' |
$(el).hide() |
el.style.display = 'none' |
$.ajax() |
fetch() |
$(document).ready() |
DOMContentLoaded event |
Migration Example
// jQuery Version
$(document).ready(function() {
$('.button').on('click', function() {
$(this).toggleClass('active');
$('.content').slideToggle();
});
$.ajax({
url: '/api/data',
method: 'GET',
success: function(data) {
$('#result').html(data);
}
});
});
// Vanilla JavaScript Version
document.addEventListener('DOMContentLoaded', function() {
const buttons = document.querySelectorAll('.button');
buttons.forEach(button => {
button.addEventListener('click', function() {
this.classList.toggle('active');
const content = document.querySelector('.content');
// For slide toggle, use CSS transitions
content.classList.toggle('is-visible');
});
});
fetch('/api/data')
.then(response => response.text())
.then(data => {
document.getElementById('result').innerHTML = data;
})
.catch(error => console.error('Error:', error));
});
Common jQuery Issues in WordPress
Troubleshooting jQuery Problems
// Issue 1: $ is not defined
// Solution: Use proper no-conflict wrapper
(function($) {
// Your code here
})(jQuery);
// Issue 2: Code runs before DOM is ready
// Solution: Use document ready
jQuery(document).ready(function($) {
// DOM is ready
});
// Issue 3: Events not working on dynamic content
// Solution: Use event delegation
$(document).on('click', '.dynamic-element', function() {
// This works for elements added later
});
// Issue 4: jQuery version conflicts
// Solution: Check version and use appropriate methods
if (typeof jQuery !== 'undefined') {
console.log('jQuery version:', jQuery.fn.jquery);
// Check for specific version
if (jQuery.fn.jquery.match(/^3\./)) {
// jQuery 3.x specific code
}
}
// Issue 5: Multiple jQuery versions loaded
// Solution: Use specific version namespace
const $j = jQuery.noConflict();
$j(document).ready(function() {
// Use $j instead of $ or jQuery
});
// Debug helper
window.jQueryDebug = function() {
console.log('jQuery loaded:', typeof jQuery !== 'undefined');
console.log('$ defined:', typeof $ !== 'undefined');
if (typeof jQuery !== 'undefined') {
console.log('jQuery version:', jQuery.fn.jquery);
console.log('jQuery plugins:', Object.keys(jQuery.fn).length);
}
};
Use browser console to check if jQuery is loaded:
console.log(jQuery.fn.jquery) will show the version if jQuery is available.
Best Practices
jQuery Best Practices in WordPress
- Always use no-conflict mode: Wrap code in proper closure
- Use WordPress's jQuery: Don't load external versions unless necessary
- Cache jQuery objects: Store selections in variables
- Chain methods: Take advantage of jQuery's chaining
- Use event delegation: For dynamic content
- Minimize DOM manipulation: Batch operations when possible
- Check element existence: Before applying operations
- Use data attributes: For storing data on elements
- Consider vanilla JS: For simple operations
- Test across versions: Ensure compatibility
jQuery may be removed from WordPress core in the future. Consider using vanilla JavaScript for new projects and gradually migrating existing jQuery code.
Practice Exercise
Build jQuery Components