Complete jQuery Tutorial
Master jQuery — the "write less, do more" JavaScript library.
Getting Started with jQuery
Set up jQuery and write your first code
What is jQuery?
jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler.
Installing jQuery
<!-- Include jQuery from CDN -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- Or use slim version (no Ajax/effects) -->
<script src="https://code.jquery.com/jquery-3.7.1.slim.min.js"></script>
<!-- Place before closing body tag -->
<body>
<!-- Your HTML content -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="script.js"></script>
</body>
Your First jQuery Code
// Wait for document to be ready
$(document).ready(function() {
// jQuery code goes here
console.log('jQuery is ready!');
});
// Shorthand version
$(function() {
console.log('jQuery is ready!');
});
// Hide an element
$(document).ready(function() {
$('#myElement').hide();
});
Basic jQuery Syntax
// Basic syntax: $(selector).action()
$('p').hide(); // Hide all paragraphs
$('#myId').show(); // Show element with ID
$('.myClass').fadeOut(); // Fade out elements with class
// Example: Change text on button click
$(document).ready(function() {
$('button').click(function() {
$('p').text('Text changed!');
});
});
Complete Example
<!DOCTYPE html>
<html>
<head>
<title>jQuery Example</title>
</head>
<body>
<h1>Hello jQuery!</h1>
<p id="demo">This is a paragraph.</p>
<button id="btn">Click Me</button>
<!-- Include jQuery -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
$(document).ready(function() {
$('#btn').click(function() {
$('#demo').text('Text changed with jQuery!');
});
});
</script>
</body>
</html>
Practice Tasks
- Create an HTML page and include jQuery via CDN.
- Write a document ready function.
- Select an element and change its text.
- Add a click event to a button.
Key Takeaways
- Include jQuery via CDN before your custom scripts.
- Use $(document).ready() to wait for DOM to load.
- Basic syntax: $(selector).action().
- jQuery simplifies JavaScript DOM manipulation.
What's Next?
Next Topic: Learn about jQuery Introduction and its features.
jQuery Introduction: Powerful JavaScript Library
Discover why jQuery revolutionized web development
What is jQuery?
jQuery is a lightweight JavaScript library with the motto "Write less, do more." It simplifies HTML document traversing, event handling, animating, and Ajax interactions.
Why Use jQuery?
✓ Cross-browser Compatibility - Works on all modern browsers
✓ DOM Manipulation - Easy to select and modify elements
✓ Event Handling - Simplified event management
✓ Ajax Support - Simplified asynchronous requests
✓ Animations - Built-in effects and animations
✓ Extensibility - Thousands of plugins available
✓ Large Community - Extensive documentation and support
✓ Lightweight - Small file size (~30KB minified)
jQuery vs Vanilla JavaScript
// Vanilla JavaScript
document.getElementById('myElement').style.display = 'none';
document.querySelectorAll('.myClass').forEach(function(el) {
el.classList.add('active');
});
// jQuery (much shorter!)
$('#myElement').hide();
$('.myClass').addClass('active');
// Ajax comparison
// Vanilla JavaScript
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data', true);
xhr.onload = function() {
console.log(xhr.responseText);
};
xhr.send();
// jQuery
$.get('/api/data', function(data) {
console.log(data);
});
Core jQuery Features
// 1. DOM Selection
$('p'); // Select all paragraphs
$('#myId'); // Select by ID
$('.myClass'); // Select by class
// 2. DOM Manipulation
$('p').text('New text'); // Change text
$('div').html('<b>Bold</b>'); // Change HTML
$('p').addClass('active'); // Add class
// 3. Event Handling
$('button').click(function() {
alert('Clicked!');
});
// 4. Effects and Animations
$('div').fadeIn();
$('p').slideUp();
$('img').animate({ opacity: 0.5 });
// 5. Ajax
$.ajax({
url: '/api/data',
method: 'GET',
success: function(data) {
console.log(data);
}
});
jQuery Versions
| Version | Release | Key Features |
|---|---|---|
| 3.x | Current | Modern browsers, improved performance |
| 2.x | Legacy | Dropped IE 6-8 support |
| 1.x | Legacy | Full IE support |
Practice Tasks
- Compare jQuery and vanilla JavaScript for DOM manipulation.
- Write code to select multiple elements.
- Add and remove classes using jQuery.
- Create a simple animation effect.
Key Takeaways
- jQuery simplifies JavaScript with concise syntax.
- Works consistently across all browsers.
- Provides powerful selectors and DOM manipulation.
- Includes built-in Ajax and animation support.
What's Next?
Next Topic: Learn about jQuery History and evolution.
jQuery History: Evolution of the Library
Journey through jQuery's development and impact
The Birth of jQuery
jQuery was created by John Resig in 2006 to simplify client-side scripting. It quickly became the most popular JavaScript library.
jQuery Timeline
| Year | Version | Major Changes |
|---|---|---|
| 2006 | 1.0 | Initial release by John Resig |
| 2009 | 1.3 | Sizzle selector engine introduced |
| 2011 | 1.6 | attr() vs prop() distinction |
| 2013 | 2.0 | Dropped IE 6-8 support |
| 2016 | 3.0 | Major rewrite, improved performance |
| 2023 | 3.7.x | Current version with modern features |
Key Milestones
2006 - jQuery 1.0 Released
• Simplified DOM manipulation
• CSS selector support
• Basic effects and animations
2009 - Sizzle Selector Engine
• Faster CSS selector parsing
• Standalone selector engine
• Used by other libraries
2013 - jQuery 2.0
• Removed legacy IE support
• Smaller file size
• Better performance
2016 - jQuery 3.0
• Promise/A+ compatible Deferred
• requestAnimationFrame for animations
• Strict mode compliance
2023 - Modern jQuery
• ES6+ compatibility
• Smaller footprint
• Focus on modern browsers
jQuery's Impact on Web Development
Before jQuery:
• Complex cross-browser code
• Verbose DOM manipulation
• Difficult Ajax implementations
• Limited animation capabilities
After jQuery:
• Simple, consistent API
• "Write less, do more" philosophy
• Cross-browser compatibility
• Rich plugin ecosystem
• Standardized web development practices
Legacy:
• Influenced modern JavaScript APIs
• Inspired frameworks like Zepto.js
• Shaped ES6+ features
• Still used in millions of websites
jQuery Today
// Modern Usage Considerations
// jQuery is still relevant for:
// - Legacy project maintenance
// - Quick prototyping
// - Plugin-rich ecosystems
// - Simpler syntax for common tasks
// Alternatives in Modern Development:
// - Vanilla JavaScript (improved APIs)
// - React, Vue, Angular (component frameworks)
// - Fetch API (for Ajax)
// - CSS animations (for effects)
// jQuery Still Powers:
// - WordPress (core dependency)
// - Bootstrap 4 and earlier
// - Thousands of existing plugins
// - Millions of websites worldwide
Practice Tasks
- Research jQuery's market share over time.
- Compare jQuery 1.x, 2.x, and 3.x features.
- Identify modern JavaScript features inspired by jQuery.
- Explore jQuery's GitHub repository and history.
Key Takeaways
- jQuery was released in 2006 by John Resig.
- It revolutionized web development with simple syntax.
- Version 3.x is the current major version.
- jQuery influenced modern JavaScript standards.
What's Next?
Next Topic: Learn jQuery Syntax and Structure.
jQuery Syntax and Structure
Master the fundamental syntax patterns of jQuery
jQuery Syntax
jQuery syntax is designed for selecting HTML elements and performing actions on them. The basic syntax is: $(selector).action()
The jQuery Object
// $ is an alias for jQuery
$('p'); // Same as jQuery('p')
jQuery('p'); // Full syntax
// Avoid conflicts with other libraries
jQuery.noConflict();
jQuery('p').hide(); // Use jQuery instead of $
// Or create custom alias
var jq = jQuery.noConflict();
jq('p').hide();
Basic Syntax Pattern
// Syntax: $(selector).action()
// ↑ ↑
// | └─ Method to perform
// └─ Element(s) to select
$('p').hide(); // Hide all paragraphs
$('#myId').show(); // Show element with ID
$('.myClass').fadeOut(); // Fade out class elements
$('div.container').css('color', 'red'); // Change CSS
// Multiple actions (chaining)
$('p').css('color', 'blue').slideUp().slideDown();
Document Ready
// Full syntax
$(document).ready(function() {
// Code here runs after DOM is ready
$('p').text('DOM is ready!');
});
// Shorthand syntax (recommended)
$(function() {
// Code here runs after DOM is ready
$('p').text('DOM is ready!');
});
// Why it's important
// BAD - may not work if DOM not loaded:
script in <head>
$('p').hide(); // Elements may not exist yet
// GOOD - waits for DOM:
$(function() {
$('p').hide(); // Elements are loaded
});
Method Chaining
// Without chaining
$('p').css('color', 'red');
$('p').slideUp();
$('p').slideDown();
// With chaining (better!)
$('p').css('color', 'red').slideUp().slideDown();
// Multi-line chaining for readability
$('#myDiv')
.css('background', 'blue')
.height(100)
.width(200)
.fadeIn(1000)
.delay(500)
.fadeOut(1000);
// Break chain when needed
var myElement = $('p');
myElement.css('color', 'red');
if (someCondition) {
myElement.hide();
}
jQuery Object vs DOM Element
// jQuery object (wrapped)
var jqElement = $('#myElement');
jqElement.hide(); // jQuery method
jqElement.addClass('active'); // jQuery method
// DOM element (unwrapped)
var domElement = document.getElementById('myElement');
domElement.style.display = 'none'; // Native JavaScript
// Convert jQuery to DOM
var dom = $('#myElement')[0]; // First element
var dom = $('#myElement').get(0); // Same result
// Convert DOM to jQuery
var jq = $(domElement);
// Check if jQuery object
if (jqElement instanceof jQuery) {
console.log('This is a jQuery object');
}
jQuery Syntax Best Practices
// 1. Cache jQuery selections
// BAD
$('.myClass').css('color', 'red');
$('.myClass').show();
$('.myClass').fadeIn();
// GOOD
var myElements = $('.myClass');
myElements.css('color', 'red');
myElements.show();
myElements.fadeIn();
// 2. Use chaining
$('.myClass').css('color', 'red').show().fadeIn();
// 3. Prefix jQuery variables with $
var $myElements = $('.myClass');
var $button = $('#btn');
// 4. Use const for jQuery objects that won't change
const $header = $('#header');
const $sidebar = $('.sidebar');
Practice Tasks
- Write code using $(selector).action() syntax.
- Create a document ready function.
- Practice method chaining with 3+ methods.
- Cache jQuery selections for better performance.
Key Takeaways
- Basic syntax: $(selector).action().
- Always use $(document).ready() or $() shorthand.
- Chain methods for concise code.
- Cache jQuery selections for performance.
What's Next?
Next Topic: Learn jQuery Selectors for finding elements.
jQuery Selectors: Finding Elements
Master powerful selectors to find any element
Selector Basics
jQuery uses CSS-style selectors to find HTML elements. The Sizzle selector engine makes it fast and powerful.
Basic Selectors
// Element selector
$('p'); // All <p> elements
$('div'); // All <div> elements
$('h1'); // All <h1> elements
// ID selector
$('#myId'); // Element with id="myId"
$('#header'); // Element with id="header"
// Class selector
$('.myClass'); // All elements with class="myClass"
$('.active'); // All elements with class="active"
// Multiple selectors
$('p, div, span'); // All p, div, AND span elements
Attribute Selectors
// Has attribute
$('[href]'); // Elements with href attribute
$('[data-id]'); // Elements with data-id attribute
// Attribute equals
$('[type="text"]'); // Input with type="text"
$('[name="email"]'); // Elements with name="email"
// Attribute contains
$('[href*="jquery"]'); // href contains "jquery"
$('[class*="btn"]'); // class contains "btn"
// Attribute starts with
$('[href^="https"]'); // href starts with "https"
$('[id^="section"]'); // id starts with "section"
// Attribute ends with
$('[src$=".jpg"]'); // src ends with ".jpg"
$('[href$=".pdf"]'); // href ends with ".pdf"
Hierarchy Selectors
// Descendant selector (space)
$('div p'); // All <p> inside <div> (any level)
$('ul li'); // All <li> inside <ul>
// Direct child selector (>)
$('div > p'); // Direct <p> children of <div>
$('ul > li'); // Direct <li> children of <ul>
// Next sibling (+)
$('h2 + p'); // <p> immediately after <h2>
// All siblings (~)
$('h2 ~ p'); // All <p> siblings after <h2>
Filter Selectors
// Position filters
$('li:first'); // First <li> element
$('li:last'); // Last <li> element
$('li:even'); // Even-indexed <li> (0, 2, 4...)
$('li:odd'); // Odd-indexed <li> (1, 3, 5...)
$('li:eq(2)'); // Third <li> (zero-indexed)
$('li:gt(2)'); // All <li> after index 2
$('li:lt(2)'); // All <li> before index 2
// Content filters
$('div:contains("hello")'); // Divs containing "hello"
$('div:empty'); // Empty divs
$('div:has(p)'); // Divs containing <p>
// Visibility filters
$(':hidden'); // Hidden elements
$(':visible'); // Visible elements
// Form filters
$(':input'); // All input elements
$(':text'); // Text inputs
$(':password'); // Password inputs
$(':checkbox'); // Checkboxes
$(':checked'); // Checked checkboxes/radios
$(':selected'); // Selected options
$(':disabled'); // Disabled form elements
$(':enabled'); // Enabled form elements
Combining Selectors
// Multiple classes
$('.class1.class2'); // Elements with BOTH classes
// Element with class
$('p.intro'); // <p> with class="intro"
$('div.container'); // <div> with class="container"
// Nested combinations
$('div.container > p.text'); // Direct p.text children of div.container
$('#header .nav li a'); // Links in nav inside header
// Multiple conditions
$('input[type="text"][name="email"]'); // Text input named "email"
$('a[href^="http"][target="_blank"]'); // External links in new tab
// Exclude with :not()
$('li:not(.active)'); // All <li> except those with class="active"
$('input:not(:disabled)'); // Enabled inputs only
Practice Tasks
- Select all paragraphs with a specific class.
- Find all links that open in a new tab.
- Select even rows in a table.
- Find all checked checkboxes in a form.
Key Takeaways
- Use CSS-style selectors to find elements.
- Combine selectors for precise targeting.
- Attribute selectors provide powerful filtering.
- jQuery extends CSS with custom selectors like :first, :even.
What's Next?
Next Topic: Learn jQuery Events for user interactions.
DOM Manipulation: Change Page Content
Add, remove, and modify HTML elements dynamically
DOM Manipulation Basics
jQuery makes DOM manipulation simple with methods to get/set content, add/remove elements, and modify attributes.
Getting and Setting Content
// Get text content
var text = $('p').text();
// Set text content
$('p').text('New text content');
// Get HTML content
var html = $('div').html();
// Set HTML content
$('div').html('<strong>Bold text</strong>');
// Get input value
var value = $('#email').val();
// Set input value
$('#email').val('user@example.com');
// Chaining
$('#demo').text('Hello').css('color', 'red');
Adding Elements
// Append (add to end inside)
$('ul').append('<li>New item</li>');
$('div').append('<p>New paragraph</p>');
// Prepend (add to beginning inside)
$('ul').prepend('<li>First item</li>');
// After (add after element)
$('h1').after('<p>Paragraph after h1</p>');
// Before (add before element)
$('h1').before('<p>Paragraph before h1</p>');
// Add multiple elements
$('ul').append(
'<li>Item 1</li>',
'<li>Item 2</li>',
'<li>Item 3</li>'
);
// Create and add element
var newDiv = $('<div></div>').text('New div');
$('body').append(newDiv);
Removing Elements
// Remove element (including data and events)
$('#myElement').remove();
$('.myClass').remove();
// Remove filtered elements
$('p').remove('.special'); // Remove p with class="special"
// Detach (remove but keep data and events)
var detached = $('p').detach();
$('body').append(detached); // Re-add with events intact
// Empty (remove child elements)
$('div').empty(); // Remove all children of div
// Remove specific child
$('ul li:first').remove(); // Remove first li
$('ul li:last').remove(); // Remove last li
Replacing Elements
// Replace element with new content
$('p').replaceWith('<h2>New heading</h2>');
// Replace all matching elements
$('.old').replaceWith('<div class="new">New content</div>');
// replaceAll (reverse syntax)
$('<h2>New heading</h2>').replaceAll('p');
Cloning Elements
// Clone element
var clone = $('#myDiv').clone();
$('body').append(clone);
// Clone with events
var cloneWithEvents = $('#myDiv').clone(true);
$('body').append(cloneWithEvents);
// Clone and modify
var newItem = $('ul li:first').clone();
newItem.text('Cloned item');
$('ul').append(newItem);
Wrapping Elements
// Wrap element
$('p').wrap('<div class="wrapper"></div>');
// Wrap all elements together
$('p').wrapAll('<div class="container"></div>');
// Wrap inner content
$('p').wrapInner('<strong></strong>');
// Unwrap (remove parent)
$('p').unwrap(); // Remove parent wrapper
Practice Tasks
- Create a to-do list with add/remove functionality.
- Change paragraph text on button click.
- Clone a div and append it to the page.
- Empty a container and add new content.
Key Takeaways
- Use .text() for text and .html() for HTML content.
- append(), prepend(), after(), before() add elements.
- .remove() deletes elements permanently.
- .clone() duplicates elements for reuse.
What's Next?
Next Topic: Learn CSS Manipulation to style elements.
jQuery Events: Handle User Interactions
Respond to clicks, hovers, and other user actions
Event Handling
jQuery simplifies event handling with cross-browser compatible methods. Events trigger code when users interact with elements.
Click Events
// Basic click event
$('button').click(function() {
alert('Button clicked!');
});
// Double click
$('div').dblclick(function() {
$(this).toggleClass('active');
});
// Access event object
$('a').click(function(event) {
event.preventDefault(); // Prevent default action
console.log('Link clicked:', $(this).attr('href'));
});
// Click on dynamically added elements (delegation)
$(document).on('click', '.dynamic-button', function() {
console.log('Dynamic button clicked');
});
Mouse Events
// Hover (mouseenter + mouseleave)
$('div').hover(
function() {
// Mouse enter
$(this).css('background', 'yellow');
},
function() {
// Mouse leave
$(this).css('background', 'white');
}
);
// Mouse enter/leave separately
$('div').mouseenter(function() {
$(this).addClass('hover');
});
$('div').mouseleave(function() {
$(this).removeClass('hover');
});
// Mouse move
$(document).mousemove(function(event) {
$('#coords').text('X: ' + event.pageX + ', Y: ' + event.pageY);
});
// Mouse down/up
$('button').mousedown(function() {
$(this).css('background', 'red');
});
$('button').mouseup(function() {
$(this).css('background', 'blue');
});
Keyboard Events
// Key press
$('input').keypress(function(event) {
console.log('Key pressed:', event.which);
});
// Key down (fires first)
$('input').keydown(function(event) {
if (event.which === 13) { // Enter key
console.log('Enter pressed');
}
});
// Key up (fires after key released)
$('input').keyup(function() {
var value = $(this).val();
$('#output').text(value);
});
// Detect specific keys
$(document).keydown(function(event) {
switch(event.which) {
case 37: // Left arrow
console.log('Left');
break;
case 39: // Right arrow
console.log('Right');
break;
case 27: // Escape
console.log('Escape');
break;
}
});
Form Events
// Form submit
$('form').submit(function(event) {
event.preventDefault();
var formData = $(this).serialize();
console.log(formData);
});
// Input change
$('input').change(function() {
console.log('Value changed to:', $(this).val());
});
// Focus events
$('input').focus(function() {
$(this).css('border', '2px solid blue');
});
$('input').blur(function() {
$(this).css('border', '1px solid gray');
});
// Select event (text selection)
$('input').select(function() {
console.log('Text selected');
});
Event Methods
// on() method (recommended for all events)
$('button').on('click', function() {
console.log('Clicked');
});
// Multiple events
$('input').on('focus blur', function() {
$(this).toggleClass('active');
});
// Event with data
$('button').on('click', { name: 'John' }, function(event) {
console.log('Hello', event.data.name);
});
// One-time event
$('button').one('click', function() {
console.log('This runs only once');
});
// Remove event handler
$('button').off('click');
// Trigger event programmatically
$('button').trigger('click');
$('form').trigger('submit');
Event Delegation
// Event delegation for dynamic content
// BAD - won't work for dynamically added elements
$('.dynamic-item').click(function() {
console.log('Clicked');
});
// GOOD - works for current and future elements
$(document).on('click', '.dynamic-item', function() {
console.log('Clicked');
});
// Better - use closer parent
$('#container').on('click', '.dynamic-item', function() {
console.log('Clicked');
});
// Multiple delegated events
$('#list').on({
click: function() {
console.log('Clicked');
},
mouseenter: function() {
$(this).addClass('hover');
},
mouseleave: function() {
$(this).removeClass('hover');
}
}, 'li');
Practice Tasks
- Create a button that shows an alert on click.
- Change element color on hover.
- Display input value in real-time as user types.
- Prevent form submission and log form data.
Key Takeaways
- Use .on() for attaching event handlers.
- Event delegation works for dynamic elements.
- Use event.preventDefault() to stop default actions.
- Access event details through the event object.
What's Next?
Next Topic: Learn DOM Manipulation to change content.
Effects and Animations
Add visual effects and smooth animations to elements
Basic Effects
jQuery provides simple methods for showing, hiding, and toggling elements with built-in effects.
// Hide elements
$('#box').hide(); // Instant hide
$('#box').hide(1000); // Hide over 1 second
$('#box').hide('slow'); // Slow hide (600ms)
$('#box').hide('fast', function() {
console.log('Hidden!');
});
// Show elements
$('#box').show(); // Instant show
$('#box').show(500); // Show over 500ms
// Toggle visibility
$('#box').toggle(); // Show if hidden, hide if visible
$('#box').toggle(1000);
$('#toggleBtn').click(function() {
$('#box').toggle('slow');
});
Fading Effects
// Fade out
$('#box').fadeOut(); // Fade to transparent
$('#box').fadeOut(1000);
$('#box').fadeOut('slow', function() {
alert('Faded out!');
});
// Fade in
$('#box').fadeIn();
$('#box').fadeIn(500);
// Fade toggle
$('#box').fadeToggle(1000);
// Fade to specific opacity
$('#box').fadeTo(1000, 0.5); // 50% opacity
$('#box').fadeTo('slow', 0.2); // 20% opacity
// Example: Image gallery hover
$('.gallery img').hover(
function() { $(this).fadeTo(300, 0.7); },
function() { $(this).fadeTo(300, 1); }
);
Sliding Effects
// Slide up (hide with sliding motion)
$('#menu').slideUp();
$('#menu').slideUp(500);
$('#menu').slideUp('slow');
// Slide down (show with sliding motion)
$('#menu').slideDown();
$('#menu').slideDown(1000);
// Slide toggle
$('#menu').slideToggle();
$('#menu').slideToggle(500);
// Example: Accordion menu
$('.accordion-header').click(function() {
$(this).next('.accordion-content').slideToggle();
$(this).parent().siblings()
.find('.accordion-content').slideUp();
});
// Example: Dropdown
$('.dropdown-trigger').hover(
function() { $('.dropdown-menu').slideDown('fast'); },
function() { $('.dropdown-menu').slideUp('fast'); }
);
Custom Animations
// Basic animation
$('#box').animate({
width: '300px',
height: '300px',
opacity: 0.5
}, 1000);
// Animation with easing
$('#box').animate({
left: '250px',
top: '100px'
}, 1000, 'swing');
// Relative values
$('#box').animate({
left: '+=50px', // Move right 50px
top: '-=20px' // Move up 20px
}, 500);
// Sequential animations
$('#box')
.animate({ width: '200px' }, 500)
.animate({ height: '200px' }, 500)
.animate({ left: '100px' }, 500);
// Toggle animation
$('#box').animate({
width: 'toggle',
height: 'toggle'
}, 1000);
Animation Queue and Control
// Stop current animation
$('#box').stop(); // Stop, remain at current position
$('#box').stop(true); // Stop, clear queue
$('#box').stop(true, true); // Stop, jump to end
// Delay animation
$('#box')
.fadeIn(500)
.delay(1000) // Wait 1 second
.fadeOut(500);
// Finish animation
$('#box').finish(); // Jump to end of all animations
// Check if animating
if (!$('#box').is(':animated')) {
$('#box').animate({ left: '100px' }, 500);
}
// Example: Button hover - prevent queue buildup
$('.btn').hover(
function() {
$(this).stop().animate({ paddingLeft: '20px' }, 200);
},
function() {
$(this).stop().animate({ paddingLeft: '10px' }, 200);
}
);
Complete Animation Example
// Comprehensive animation
$('#animateBtn').click(function() {
$('#box')
.fadeOut(300)
.fadeIn(300)
.animate({
marginLeft: '200px',
opacity: 0.5
}, 1000)
.animate({
marginLeft: '0',
opacity: 1
}, 1000, function() {
$(this).css('background', 'green');
alert('Animation complete!');
});
});
// Custom easing function
$.easing.custom = function(x, t, b, c, d) {
return c * (t /= d) * t + b;
};
$('#box').animate(
{ left: '300px' },
1500,
'custom'
);
// Animate colors (requires jQuery UI)
$('#box').animate({
backgroundColor: '#ff0000',
color: '#ffffff'
}, 1000);
Best Practices
// Cache selectors
var $box = $('#box');
$box.fadeIn().animate({ left: '100px' });
// Use CSS transitions when possible
// jQuery for compatibility, CSS for performance
// Avoid animating layout properties frequently
// Prefer opacity and transform
// Stop before new animation
$box.stop(true, true).fadeIn();
// Use callbacks efficiently
$box.fadeOut(500, function() {
$(this).remove(); // Clean up
});
Practice Tasks
- Create a fade-in effect on page load.
- Build a slide toggle menu system.
- Animate an element from left to right.
- Create a loading spinner with animations.
Key Takeaways
- Use fadeIn(), fadeOut(), slideUp(), slideDown() for common effects.
- animate() customizes property transitions.
- stop() prevents animation queue buildup.
- Chain effects for complex sequences.
- Use callbacks to execute code after animations.
What's Next?
Next Topic: Learn Ajax for Asynchronous Requests.
Ajax: Asynchronous Requests
Load data from servers without page refresh
What is Ajax?
Ajax (Asynchronous JavaScript and XML) allows you to load data from a server in the background and update parts of the page without reloading.
Basic Ajax Request
// Basic Ajax call
$.ajax({
url: '/api/data',
method: 'GET',
success: function(response) {
console.log('Success:', response);
$('#result').html(response);
},
error: function(xhr, status, error) {
console.log('Error:', error);
$('#result').html('Failed to load data');
}
});
// With more options
$.ajax({
url: '/api/users',
method: 'POST',
data: { name: 'John', age: 30 },
dataType: 'json',
timeout: 5000,
beforeSend: function() {
$('#loading').show();
},
success: function(response) {
console.log(response);
},
error: function(xhr, status, error) {
alert('Request failed: ' + error);
},
complete: function() {
$('#loading').hide();
}
});
Shorthand Methods
// GET request
$.get('/api/data', function(response) {
$('#result').html(response);
});
// GET with parameters
$.get('/api/users', { id: 123 }, function(response) {
console.log(response);
});
// POST request
$.post('/api/submit', { name: 'John', email: 'john@example.com' },
function(response) {
alert('Submitted successfully');
}
);
// POST with error handling
$.post('/api/login', { username: 'user', password: 'pass' })
.done(function(response) {
console.log('Login successful');
window.location = '/dashboard';
})
.fail(function() {
alert('Login failed');
});
// Chained handlers
$.get('/api/data')
.done(function(data) {
console.log('Success:', data);
})
.fail(function(xhr, status, error) {
console.log('Error:', error);
})
.always(function() {
console.log('Request completed');
});
Load HTML Content
// Load content into element
$('#content').load('/pages/about.html');
// Load specific element from page
$('#content').load('/pages/about.html #main');
// Load with callback
$('#content').load('/pages/about.html', function(response, status, xhr) {
if (status === 'success') {
console.log('Content loaded');
} else {
console.log('Error loading content');
}
});
// Load with data
$('#content').load('/pages/search.html', { query: 'jquery' });
// Example: Tab system
$('.tab').click(function() {
var page = $(this).data('page');
$('#tabContent').load('/pages/' + page + '.html');
});
JSON Data
// Get JSON data
$.getJSON('/api/users.json', function(users) {
$.each(users, function(index, user) {
$('#userList').append(
'<li>' + user.name + ' - ' + user.email + '</li>'
);
});
});
// With parameters
$.getJSON('/api/search', { query: 'jquery' }, function(results) {
console.log(results);
});
// Parse JSON response
$.ajax({
url: '/api/data',
dataType: 'json',
success: function(data) {
var name = data.user.name;
var email = data.user.email;
$('#info').html(name + ' - ' + email);
}
});
// Send JSON data
$.ajax({
url: '/api/users',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ name: 'John', age: 30 }),
success: function(response) {
console.log('User created');
}
});
Form Submission
// Serialize and submit form
$('#myForm').submit(function(e) {
e.preventDefault();
$.ajax({
url: $(this).attr('action'),
method: 'POST',
data: $(this).serialize(),
success: function(response) {
$('#message').html('Form submitted successfully');
$('#myForm')[0].reset();
},
error: function() {
$('#message').html('Submission failed');
}
});
});
// With FormData (for file uploads)
$('#uploadForm').submit(function(e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: '/api/upload',
method: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
alert('Upload successful');
}
});
});
// Live search
$('#searchInput').keyup(function() {
var query = $(this).val();
if (query.length >= 3) {
$.get('/api/search', { q: query }, function(results) {
$('#searchResults').html('');
$.each(results, function(index, item) {
$('#searchResults').append('<div>' + item + '</div>');
});
});
}
});
Error Handling
// Detailed error handling
$.ajax({
url: '/api/data',
success: function(data) {
console.log('Success');
},
error: function(xhr, status, error) {
console.log('Status:', status);
console.log('Error:', error);
console.log('Status Code:', xhr.status);
console.log('Response:', xhr.responseText);
if (xhr.status === 404) {
alert('Resource not found');
} else if (xhr.status === 500) {
alert('Server error');
} else {
alert('Request failed');
}
},
timeout: 3000,
statusCode: {
404: function() {
alert('Page not found');
},
500: function() {
alert('Server error');
}
}
});
// Global error handler
$(document).ajaxError(function(event, xhr, settings, error) {
console.log('Ajax error:', error);
});
// Retry on failure
function ajaxWithRetry(url, retries) {
return $.ajax(url).retry({ times: retries, timeout: 1000 });
}
Global Ajax Events
// Before any Ajax request
$(document).ajaxStart(function() {
$('#loadingSpinner').show();
});
// After all Ajax requests complete
$(document).ajaxStop(function() {
$('#loadingSpinner').hide();
});
// Before each request
$(document).ajaxSend(function(event, xhr, settings) {
console.log('Request to:', settings.url);
// Add CSRF token
xhr.setRequestHeader('X-CSRF-TOKEN', $('meta[name="csrf-token"]').attr('content'));
});
// After successful request
$(document).ajaxSuccess(function(event, xhr, settings) {
console.log('Success:', settings.url);
});
// After failed request
$(document).ajaxError(function(event, xhr, settings, error) {
console.log('Error:', settings.url, error);
});
// After any request (success or error)
$(document).ajaxComplete(function(event, xhr, settings) {
console.log('Complete:', settings.url);
});
Complete Example
$(document).ready(function() {
// Setup
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
timeout: 10000
});
// Load initial data
loadUsers();
// Submit form
$('#addUserForm').submit(function(e) {
e.preventDefault();
var userData = {
name: $('#name').val(),
email: $('#email').val()
};
$.post('/api/users', userData)
.done(function(response) {
alert('User added');
$('#addUserForm')[0].reset();
loadUsers(); // Refresh list
})
.fail(function(xhr) {
alert('Error: ' + xhr.responseJSON.message);
});
});
// Delete user
$(document).on('click', '.deleteUser', function() {
var userId = $(this).data('id');
if (confirm('Delete user?')) {
$.ajax({
url: '/api/users/' + userId,
method: 'DELETE',
success: function() {
alert('User deleted');
loadUsers();
}
});
}
});
// Load users function
function loadUsers() {
$.getJSON('/api/users', function(users) {
$('#userList').html('');
$.each(users, function(index, user) {
$('#userList').append(
'<div class="user">' +
user.name + ' - ' + user.email +
'<button class="deleteUser" data-id="' + user.id + '">Delete</button>' +
'</div>'
);
});
});
}
});
Practice Tasks
- Load JSON data and display in a list.
- Create a live search feature.
- Submit a form via Ajax with validation.
- Implement loading spinner for Ajax requests.
Key Takeaways
- $.ajax() is the core method for all Ajax requests.
- Use $.get() and $.post() for simple requests.
- $.getJSON() loads JSON data easily.
- Handle errors with .fail() or error callback.
- Use global events to track all Ajax activity.
What's Next?
Next Topic: Learn Deferred and Promises.
Traversing: Navigate the DOM
Move through HTML elements to find parents, children, and siblings
DOM Traversing
Traversing allows you to navigate from selected elements to related elements in the DOM tree - parents, children, siblings, and descendants.
Parent Elements
// Direct parent
var parent = $('p').parent();
$('span').parent().css('border', '1px solid red');
// All ancestors (parents, grandparents, etc.)
$('span').parents().css('color', 'blue');
// Ancestors up to specific element
$('span').parentsUntil('div');
// Filtered parents
$('span').parents('.container');
$('p').parent('div');
// Closest ancestor matching selector
$('span').closest('div'); // First div ancestor
$('li').closest('ul');
Child Elements
// Direct children
$('div').children(); // All direct children
$('ul').children('li'); // Only <li> children
// All descendants
$('div').find('p'); // All <p> inside div
$('ul').find('li.active'); // All li.active in ul
// First/last child
$('ul').children(':first');
$('ul').children(':last');
// Filter children
$('ul').children().filter('.active');
$('div').children().not('.exclude');
Sibling Elements
// All siblings
$('li.active').siblings(); // All other li elements
$('h2').siblings('p'); // Only p siblings
// Next sibling
$('li').next(); // Next li
$('h2').next('p'); // Next p after h2
// All next siblings
$('li:first').nextAll();
$('h2').nextAll('p');
// Next siblings until
$('li:first').nextUntil('li.last');
// Previous sibling
$('li').prev();
$('h2').prev('p');
// All previous siblings
$('li:last').prevAll();
// Previous siblings until
$('li:last').prevUntil('li:first');
Filtering
// Filter by selector
$('p').filter('.intro'); // Only p with class intro
$('li').filter(':even'); // Even-indexed li
// Filter by function
$('div').filter(function() {
return $(this).children().length > 2;
});
// Not filter (exclude)
$('p').not('.exclude');
$('li').not(':first');
// Has (elements containing selector)
$('div').has('p'); // Divs containing p
$('ul').has('li.active');
// Is (check if matches)
if ($('p').is('.active')) {
console.log('Has active class');
}
Selecting Specific Elements
// First element
$('li').first(); // First li
$('p').first().css('font-weight', 'bold');
// Last element
$('li').last();
$('p').last().css('color', 'red');
// Element by index
$('li').eq(2); // Third li (zero-indexed)
$('p').eq(0); // First p
$('p').eq(-1); // Last p
// Slice range
$('li').slice(1, 4); // li from index 1 to 3
$('p').slice(2); // All p from index 2 onwards
Combining Traversal Methods
// Complex traversal
$('li.active')
.parent()
.siblings()
.find('span')
.css('color', 'red');
// Find and filter
$('div')
.children()
.filter('.highlight')
.addClass('active');
// Navigate and modify
$('p:first')
.nextAll()
.slice(0, 3)
.addClass('group');
// End() - return to previous selection
$('div')
.find('p').css('color', 'blue')
.end() // Back to div
.css('border', '1px solid red');
// Add to selection
$('div').add('p').css('background', 'yellow');
Practice Tasks
- Find all siblings of a clicked element.
- Navigate from child to parent and style it.
- Get all children of a div and add a class.
- Filter list items and highlight selected ones.
Key Takeaways
- Use parent(), children(), siblings() to navigate.
- find() searches all descendants.
- filter(), not() refine selections.
- Chain traversal methods for complex navigation.
What's Next?
Next Topic: Learn Effects and Animations.
Attributes and Properties
Work with HTML attributes and DOM properties
Attributes vs Properties
Attributes are defined in HTML. Properties are defined in DOM. Use attr() for attributes, prop() for properties.
// Attribute: What's in the HTML
<input type="checkbox" checked="checked" id="cb">
// Property: Current DOM state
var isChecked = $('#cb').prop('checked'); // true or false
// Attribute stays same, property changes
$('#cb').click(function() {
console.log($(this).attr('checked')); // 'checked' (unchanged)
console.log($(this).prop('checked')); // true/false (changes)
});
// When to use which:
// - Use prop() for: checked, selected, disabled, readOnly
// - Use attr() for: href, src, title, data-*, custom attributes
Working with Attributes
// Get attribute
var href = $('a').attr('href');
var src = $('img').attr('src');
var title = $('#box').attr('title');
// Set single attribute
$('img').attr('alt', 'Image description');
$('a').attr('href', 'https://example.com');
// Set multiple attributes
$('img').attr({
src: 'image.jpg',
alt: 'Description',
title: 'Image Title'
});
// Set attribute with function
$('img').attr('alt', function(index, currentValue) {
return 'Image ' + (index + 1);
});
// Remove attribute
$('img').removeAttr('title');
$('a').removeAttr('target');
Working with Properties
// Get property
var isChecked = $('#checkbox').prop('checked');
var isDisabled = $('#input').prop('disabled');
var isSelected = $('#option').prop('selected');
// Set property
$('#checkbox').prop('checked', true);
$('#input').prop('disabled', false);
$('#option').prop('selected', true);
// Set multiple properties
$('#input').prop({
disabled: false,
readOnly: true
});
// Toggle property
var checked = $('#cb').prop('checked');
$('#cb').prop('checked', !checked);
// Remove property
$('#input').removeProp('disabled');
Data Attributes
// HTML: <div id="user" data-id="123" data-role="admin">
// Get data attribute
var userId = $('#user').data('id'); // 123 (number)
var role = $('#user').data('role'); // 'admin'
// Set data attribute
$('#user').data('id', 456);
$('#user').data('email', 'user@example.com');
// Set multiple data
$('#user').data({
id: 789,
role: 'user',
active: true
});
// Get all data
var allData = $('#user').data(); // Object with all data
// Remove data
$('#user').removeData('role');
// Note: data() converts types automatically
// data-count="5" becomes number 5
// data-active="true" becomes boolean true
Class Manipulation
// Add class
$('#box').addClass('active');
$('#box').addClass('highlight important');
// Remove class
$('#box').removeClass('active');
$('#box').removeClass('highlight important');
$('#box').removeClass(); // Remove all classes
// Toggle class
$('#box').toggleClass('active');
$('#box').toggleClass('highlight', true); // Force add
// Check if has class
if ($('#box').hasClass('active')) {
console.log('Element is active');
}
// Add class with function
$('li').addClass(function(index) {
return 'item-' + index;
});
// Multiple operations
$('#box')
.addClass('show')
.removeClass('hide')
.toggleClass('active');
Value Manipulation
// Get value
var inputValue = $('#name').val();
var selectedOption = $('#country').val();
var checkedValues = $('input:checked').map(function() {
return $(this).val();
}).get();
// Set value
$('#name').val('John Doe');
$('#country').val('USA');
$('#checkbox').val(['option1', 'option2']);
// Clear value
$('#name').val('');
$('input[type="text"]').val('');
// Set with function
$('input').val(function(index, currentValue) {
return currentValue.toUpperCase();
});
// Example: Form handling
$('#submitBtn').click(function() {
var name = $('#name').val();
var email = $('#email').val();
var message = $('#message').val();
console.log({ name, email, message });
});
Practical Examples
// Toggle checkbox
$('#toggleAll').change(function() {
$('.item-checkbox').prop('checked', $(this).prop('checked'));
});
// Disable submit button
if ($('#terms').prop('checked')) {
$('#submit').prop('disabled', false);
} else {
$('#submit').prop('disabled', true);
}
// Dynamic image source
$('.thumbnail').click(function() {
var fullImage = $(this).data('full-image');
$('#mainImage').attr('src', fullImage);
});
// Toggle button state
$('.toggle-btn').click(function() {
var isActive = $(this).hasClass('active');
$(this)
.toggleClass('active')
.attr('aria-pressed', !isActive)
.text(isActive ? 'Activate' : 'Deactivate');
});
// Store element state
$('.item').each(function(index) {
$(this).data('index', index);
$(this).data('originalColor', $(this).css('background-color'));
});
Practice Tasks
- Toggle checkbox states programmatically.
- Store and retrieve custom data on elements.
- Change image sources dynamically.
- Disable form inputs based on conditions.
Key Takeaways
- Use prop() for DOM properties (checked, disabled, selected).
- Use attr() for HTML attributes (href, src, title).
- data() stores custom data on elements.
- addClass(), removeClass(), toggleClass() manage classes.
- val() gets/sets form element values.
What's Next?
Next Topic: Learn jQuery Utilities.
CSS Manipulation: Style Elements
Change styles and classes dynamically with jQuery
CSS Manipulation
jQuery provides powerful methods to get and set CSS properties, add/remove classes, and toggle styles dynamically.
Getting and Setting CSS
// Get CSS property
var color = $('p').css('color');
var fontSize = $('h1').css('font-size');
// Set single CSS property
$('p').css('color', 'red');
$('div').css('background-color', '#f0f0f0');
// Set multiple CSS properties
$('p').css({
'color': 'blue',
'font-size': '18px',
'font-weight': 'bold',
'text-align': 'center'
});
// Using camelCase (recommended)
$('p').css({
color: 'blue',
fontSize: '18px',
fontWeight: 'bold',
backgroundColor: '#fff'
});
Class Manipulation
// Add class
$('p').addClass('highlight');
$('div').addClass('active selected'); // Multiple classes
// Remove class
$('p').removeClass('highlight');
$('div').removeClass('active selected');
// Toggle class (add if not present, remove if present)
$('p').toggleClass('active');
$('div').toggleClass('highlight');
// Check if has class
if ($('p').hasClass('active')) {
console.log('Has active class');
}
// Remove all classes
$('p').removeClass();
// Add class based on condition
$('div').toggleClass('visible', someCondition);
Dimensions
// Width and height (content only)
var width = $('div').width();
var height = $('div').height();
$('div').width(200);
$('div').height(100);
// innerWidth/Height (content + padding)
var innerWidth = $('div').innerWidth();
var innerHeight = $('div').innerHeight();
// outerWidth/Height (content + padding + border)
var outerWidth = $('div').outerWidth();
var outerHeight = $('div').outerHeight();
// outerWidth/Height including margin
var outerWidthMargin = $('div').outerWidth(true);
var outerHeightMargin = $('div').outerHeight(true);
// Set multiple dimensions
$('div').css({
width: '300px',
height: '200px'
});
Position and Offset
// Get position relative to parent
var pos = $('div').position();
console.log(pos.top, pos.left);
// Get position relative to document
var offset = $('div').offset();
console.log(offset.top, offset.left);
// Set position
$('div').offset({ top: 100, left: 50 });
// Get scroll position
var scrollTop = $(window).scrollTop();
var scrollLeft = $(window).scrollLeft();
// Set scroll position
$(window).scrollTop(0); // Scroll to top
// Smooth scroll to element
$('html, body').animate({
scrollTop: $('#section').offset().top
}, 1000);
Show and Hide
// Hide element
$('div').hide();
$('p').hide(1000); // Hide with animation (1 second)
// Show element
$('div').show();
$('p').show('slow'); // Show with slow animation
// Toggle visibility
$('div').toggle();
$('p').toggle(500); // Toggle with animation
// Fade effects
$('div').fadeOut();
$('div').fadeIn();
$('div').fadeToggle();
$('div').fadeTo(1000, 0.5); // Fade to 50% opacity
// Slide effects
$('div').slideUp();
$('div').slideDown();
$('div').slideToggle();
Custom Animations
// Basic animation
$('div').animate({
width: '300px',
height: '200px',
opacity: 0.5
}, 1000);
// Animation with callback
$('div').animate({
left: '250px',
top: '100px'
}, 'slow', function() {
alert('Animation complete!');
});
// Chained animations
$('div')
.animate({ width: '300px' }, 500)
.animate({ height: '200px' }, 500)
.animate({ opacity: 0.5 }, 500);
// Relative animation
$('div').animate({
left: '+=50px', // Move 50px right
top: '-=20px' // Move 20px up
}, 1000);
// Stop animation
$('div').stop();
$('div').stop(true, true); // Clear queue and jump to end
Practice Tasks
- Change element color on button click.
- Toggle a class to show/hide content.
- Create a fade-in/fade-out animation.
- Build a sliding sidebar menu.
Key Takeaways
- Use .css() to get/set style properties.
- addClass(), removeClass(), toggleClass() manage classes.
- width(), height(), offset() handle dimensions.
- .animate() creates custom animations.
What's Next?
Next Topic: Learn Traversing to navigate the DOM.
Dimensions and Positioning
Measure and position elements precisely
Understanding Dimensions
jQuery provides methods to get and set element dimensions: width, height, and positioning coordinates.
Width and Height
// Get width (content only)
var width = $('#box').width(); // e.g., 200
// Set width
$('#box').width(300);
$('#box').width('50%');
$('#box').width('+=50'); // Increase by 50px
// Get height (content only)
var height = $('#box').height();
// Set height
$('#box').height(400);
$('#box').height('100px');
// Set both
$('#box').width(300).height(400);
// Get dimensions with function
$('.box').width(function(index, currentWidth) {
return currentWidth * 1.5; // Increase by 50%
});
Inner and Outer Dimensions
// innerWidth = width + padding
var innerWidth = $('#box').innerWidth();
// innerHeight = height + padding
var innerHeight = $('#box').innerHeight();
// outerWidth = width + padding + border
var outerWidth = $('#box').outerWidth();
// outerWidth with margin = width + padding + border + margin
var outerWidthWithMargin = $('#box').outerWidth(true);
// outerHeight = height + padding + border
var outerHeight = $('#box').outerHeight();
// outerHeight with margin
var outerHeightWithMargin = $('#box').outerHeight(true);
// Example: Calculate total space
var totalWidth = $('#box').outerWidth(true); // Includes margin
var totalHeight = $('#box').outerHeight(true);
// Set outer dimensions
$('#box').outerWidth(500); // Sets width accounting for padding/border
$('#box').outerHeight(300);
Position: offset() and position()
// offset() - position relative to document
var offset = $('#box').offset();
console.log(offset.top); // e.g., 150
console.log(offset.left); // e.g., 200
// Set offset
$('#box').offset({ top: 100, left: 150 });
$('#box').offset(function(index, currentOffset) {
return {
top: currentOffset.top + 50,
left: currentOffset.left + 100
};
});
// position() - position relative to offset parent
var position = $('#box').position();
console.log(position.top); // Position relative to parent
console.log(position.left);
// Note: position() is read-only, use css() or offset() to set
// Example: Center element
function centerElement($element) {
var windowWidth = $(window).width();
var windowHeight = $(window).height();
var elementWidth = $element.outerWidth();
var elementHeight = $element.outerHeight();
$element.css({
position: 'absolute',
left: (windowWidth - elementWidth) / 2 + 'px',
top: (windowHeight - elementHeight) / 2 + 'px'
});
}
centerElement($('#modal'));
Scroll Position
// Get scroll position
var scrollTop = $(window).scrollTop();
var scrollLeft = $(window).scrollLeft();
// Set scroll position
$(window).scrollTop(500); // Scroll to 500px from top
$(window).scrollLeft(200);
// Scroll element container
$('#container').scrollTop(100);
// Smooth scroll to element
function scrollToElement($element) {
$('html, body').animate({
scrollTop: $element.offset().top
}, 500);
}
scrollToElement($('#section2'));
// Scroll to top button
$('#scrollToTop').click(function() {
$('html, body').animate({ scrollTop: 0 }, 800);
});
// Show button when scrolled
$(window).scroll(function() {
if ($(this).scrollTop() > 300) {
$('#scrollToTop').fadeIn();
} else {
$('#scrollToTop').fadeOut();
}
});
// Detect scroll to bottom
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
var windowHeight = $(this).height();
var docHeight = $(document).height();
if (scrollTop + windowHeight >= docHeight - 10) {
console.log('Reached bottom');
loadMoreContent();
}
});
Document and Window Dimensions
// Window (viewport) dimensions
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// Document dimensions
var docWidth = $(document).width();
var docHeight = $(document).height();
// Responsive check
if ($(window).width() < 768) {
console.log('Mobile view');
} else if ($(window).width() < 1024) {
console.log('Tablet view');
} else {
console.log('Desktop view');
}
// Update on resize
$(window).resize(function() {
var width = $(window).width();
$('#screenSize').text(width + 'px');
});
// Debounced resize
var resizeTimer;
$(window).resize(function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
console.log('Resize complete');
adjustLayout();
}, 250);
});
Offset Parent
// Get offset parent (nearest positioned ancestor)
var $parent = $('#element').offsetParent();
console.log($parent.attr('id'));
// Use for relative positioning calculations
var $element = $('#child');
var $offsetParent = $element.offsetParent();
var relativeTop = $element.offset().top - $offsetParent.offset().top;
var relativeLeft = $element.offset().left - $offsetParent.offset().left;
console.log('Relative position:', relativeTop, relativeLeft);
// Example: Position tooltip relative to button
function positionTooltip($tooltip, $button) {
var buttonOffset = $button.offset();
var buttonHeight = $button.outerHeight();
$tooltip.offset({
top: buttonOffset.top + buttonHeight + 5,
left: buttonOffset.left
});
}
Practical Examples
// Example 1: Sticky header
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
var $header = $('#header');
if (scrollTop > 100) {
$header.addClass('sticky');
} else {
$header.removeClass('sticky');
}
});
// Example 2: Dropdown positioning
function positionDropdown($trigger, $dropdown) {
var triggerOffset = $trigger.offset();
var triggerHeight = $trigger.outerHeight();
var triggerWidth = $trigger.outerWidth();
var dropdownWidth = $dropdown.outerWidth();
var windowWidth = $(window).width();
var left = triggerOffset.left;
// Check if dropdown would overflow right
if (left + dropdownWidth > windowWidth) {
left = triggerOffset.left + triggerWidth - dropdownWidth;
}
$dropdown.css({
position: 'absolute',
top: triggerOffset.top + triggerHeight + 'px',
left: left + 'px'
});
}
$('.dropdown-trigger').click(function() {
var $dropdown = $(this).next('.dropdown-menu');
positionDropdown($(this), $dropdown);
$dropdown.show();
});
// Example 3: Equal height columns
function equalizeHeights($elements) {
var maxHeight = 0;
$elements.each(function() {
var height = $(this).height();
if (height > maxHeight) {
maxHeight = height;
}
});
$elements.height(maxHeight);
}
equalizeHeights($('.column'));
// Example 4: Parallax scrolling
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
$('.parallax').css({
transform: 'translateY(' + (scrollTop * 0.5) + 'px)'
});
});
// Example 5: Lazy loading images
$(window).scroll(function() {
var windowBottom = $(this).scrollTop() + $(this).height();
$('img[data-src]').each(function() {
var $img = $(this);
var imgTop = $img.offset().top;
if (imgTop < windowBottom + 200) { // 200px threshold
$img.attr('src', $img.data('src'));
$img.removeAttr('data-src');
}
});
});
Animated Positioning
// Animate to new position
$('#box').animate({
left: '200px',
top: '150px'
}, 1000);
// Animate dimensions
$('#box').animate({
width: '300px',
height: '400px'
}, 500);
// Slide element to position
function slideToPosition($element, left, top) {
$element.animate({
left: left + 'px',
top: top + 'px'
}, 600);
}
slideToPosition($('#modal'), 100, 50);
// Bounce effect
function bounceElement($element) {
$element
.animate({ top: '-=20px' }, 200)
.animate({ top: '+=20px' }, 200)
.animate({ top: '-=10px' }, 100)
.animate({ top: '+=10px' }, 100);
}
$('#notification').click(function() {
bounceElement($(this));
});
Complete Positioning System
// Modal positioning system
function showModal($modal) {
// Get dimensions
var windowWidth = $(window).width();
var windowHeight = $(window).height();
var modalWidth = $modal.outerWidth();
var modalHeight = $modal.outerHeight();
var scrollTop = $(window).scrollTop();
// Calculate centered position
var left = (windowWidth - modalWidth) / 2;
var top = (windowHeight - modalHeight) / 2 + scrollTop;
// Ensure minimum margins
if (left < 20) left = 20;
if (top < 20) top = 20;
// Position modal
$modal.css({
position: 'absolute',
left: left + 'px',
top: top + 'px',
display: 'none'
}).fadeIn(300);
// Show overlay
$('<div class="modal-overlay"></div>')
.css({
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundColor: 'rgba(0,0,0,0.5)',
zIndex: 999
})
.hide()
.fadeIn(300)
.insertBefore($modal);
}
function closeModal($modal) {
$modal.fadeOut(300);
$('.modal-overlay').fadeOut(300, function() {
$(this).remove();
});
}
// Usage
$('.open-modal').click(function() {
showModal($('#myModal'));
});
$('.close-modal, .modal-overlay').click(function() {
closeModal($('#myModal'));
});
// Reposition on window resize
$(window).resize(function() {
if ($('#myModal').is(':visible')) {
showModal($('#myModal'));
}
});
Practice Tasks
- Create a scroll-to-top button that appears after scrolling.
- Build a positioned dropdown menu system.
- Implement equal height columns.
- Create a centered modal dialog.
Key Takeaways
- width()/height() get/set content dimensions.
- innerWidth()/innerHeight() include padding.
- outerWidth()/outerHeight() include padding + border.
- offset() gives position relative to document.
- position() gives position relative to offset parent.
- scrollTop()/scrollLeft() control scroll position.
What's Next?
Next Topic: Learn Performance Optimization.
Forms: Handling User Input
Validate, serialize, and submit forms with jQuery
Form Events
jQuery provides powerful methods to handle form submissions, input changes, and validation.
// Submit event
$('#myForm').submit(function(e) {
e.preventDefault(); // Stop default submission
console.log('Form submitted');
});
// Input change
$('#email').change(function() {
console.log('Email changed: ' + $(this).val());
});
// Input focus/blur
$('#name').focus(function() {
$(this).addClass('focused');
}).blur(function() {
$(this).removeClass('focused');
});
// Keyup - for live validation
$('#password').keyup(function() {
var strength = $(this).val().length;
$('#strength').text('Strength: ' + strength);
});
// Select change
$('#country').change(function() {
var selected = $(this).val();
console.log('Selected: ' + selected);
});
Form Serialization
// Serialize to URL-encoded string
var formData = $('#myForm').serialize();
// Result: "name=John&email=john@example.com&age=30"
// Serialize to array of objects
var formArray = $('#myForm').serializeArray();
// Result: [{name:'name', value:'John'}, {name:'email', value:'john@example.com'}]
// Convert to object
function formToObject(form) {
var data = {};
$.each(form.serializeArray(), function() {
data[this.name] = this.value;
});
return data;
}
var formObject = formToObject($('#myForm'));
// Result: {name: 'John', email: 'john@example.com', age: '30'}
// Example: Ajax form submission
$('#myForm').submit(function(e) {
e.preventDefault();
$.ajax({
url: '/api/submit',
method: 'POST',
data: $(this).serialize(),
success: function(response) {
console.log('Success:', response);
}
});
});
Form Validation
// Basic validation
function validateForm() {
var isValid = true;
// Check required fields
$('.required').each(function() {
if ($(this).val() === '') {
$(this).addClass('error');
isValid = false;
} else {
$(this).removeClass('error');
}
});
return isValid;
}
$('#myForm').submit(function(e) {
if (!validateForm()) {
e.preventDefault();
alert('Please fill all required fields');
}
});
// Email validation
function validateEmail(email) {
var regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
$('#email').blur(function() {
var email = $(this).val();
if (!validateEmail(email)) {
$(this).addClass('error');
$('#emailError').text('Invalid email address');
} else {
$(this).removeClass('error');
$('#emailError').text('');
}
});
// Password strength
$('#password').keyup(function() {
var password = $(this).val();
var strength = 0;
if (password.length >= 8) strength++;
if (/[a-z]/.test(password)) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^a-zA-Z0-9]/.test(password)) strength++;
$('#strength')
.removeClass('weak medium strong')
.addClass(strength < 3 ? 'weak' : strength < 5 ? 'medium' : 'strong')
.text(['Weak', 'Medium', 'Strong'][Math.floor(strength / 2)]);
});
Dynamic Form Fields
// Add new field
$('#addField').click(function() {
var newField = $('<div class="field">' +
'<input type="text" name="item[]" class="form-control">' +
'<button type="button" class="removeField">Remove</button>' +
'</div>');
$('#fieldContainer').append(newField);
});
// Remove field (use event delegation)
$('#fieldContainer').on('click', '.removeField', function() {
$(this).parent('.field').remove();
});
// Clone field
$('#cloneField').click(function() {
var clone = $('.field:first').clone();
clone.find('input').val(''); // Clear value
$('#fieldContainer').append(clone);
});
// Counter for fields
var fieldCount = 1;
$('#addField').click(function() {
fieldCount++;
var newField = $('<input>', {
type: 'text',
name: 'field' + fieldCount,
id: 'field' + fieldCount,
class: 'form-control'
});
$('#fieldContainer').append(newField);
});
Form Reset and Clear
// Reset form to defaults
$('#resetBtn').click(function() {
$('#myForm')[0].reset(); // Native reset
});
// Clear all inputs
$('#clearBtn').click(function() {
$('#myForm').find('input[type="text"], textarea').val('');
$('#myForm').find('input[type="checkbox"], input[type="radio"]')
.prop('checked', false);
$('#myForm').find('select').prop('selectedIndex', 0);
});
// Clear specific fields
$('#clearName').click(function() {
$('#name, #email').val('');
});
// Reset validation errors
function clearErrors() {
$('.error').removeClass('error');
$('.error-message').text('');
}
// Reset after successful submission
$('#myForm').submit(function(e) {
e.preventDefault();
// ... submit logic
$(this)[0].reset();
clearErrors();
});
Select Box Manipulation
// Get selected value
var selected = $('#country').val();
var selectedText = $('#country option:selected').text();
// Set selected option
$('#country').val('USA');
// Add new option
$('#country').append($('<option>', {
value: 'UK',
text: 'United Kingdom'
}));
// Remove option
$('#country option[value="UK"]').remove();
// Get all selected (multiple select)
var selected = $('#interests').val(); // Array
// Select multiple options
$('#interests').val(['sports', 'music', 'reading']);
// Disable/enable options
$('#country option[value="USA"]').prop('disabled', true);
// Dynamic population
var countries = ['USA', 'UK', 'Canada'];
$.each(countries, function(index, value) {
$('#country').append($('<option>', {
value: value,
text: value
}));
});
Complete Form Example
$(document).ready(function() {
$('#registrationForm').submit(function(e) {
e.preventDefault();
// Clear previous errors
$('.error').removeClass('error');
$('.error-message').hide();
var isValid = true;
// Validate name
if ($('#name').val().trim() === '') {
$('#name').addClass('error');
$('#nameError').text('Name is required').show();
isValid = false;
}
// Validate email
var email = $('#email').val();
if (!validateEmail(email)) {
$('#email').addClass('error');
$('#emailError').text('Invalid email').show();
isValid = false;
}
// Validate password
var password = $('#password').val();
if (password.length < 8) {
$('#password').addClass('error');
$('#passwordError').text('Minimum 8 characters').show();
isValid = false;
}
// Check terms
if (!$('#terms').prop('checked')) {
$('#termsError').text('Accept terms').show();
isValid = false;
}
if (isValid) {
// Submit via Ajax
$.ajax({
url: '/api/register',
method: 'POST',
data: $(this).serialize(),
success: function(response) {
alert('Registration successful!');
$('#registrationForm')[0].reset();
},
error: function() {
alert('Registration failed');
}
});
}
});
});
Practice Tasks
- Create a form with validation for email and password.
- Add dynamic fields that can be added/removed.
- Serialize form data and log to console.
- Build a select dropdown that populates another select.
Key Takeaways
- Use .submit() to handle form submissions.
- serialize() converts forms to URL-encoded strings.
- Validate inputs with custom functions and regex.
- Add/remove fields dynamically with event delegation.
- Clear forms with reset() or by setting values to empty.
What's Next?
Next Topic: Learn Chaining Methods.
jQuery Utilities
Helper functions for arrays, objects, and more
What are jQuery Utilities?
jQuery provides utility methods that help with common tasks like iterating, type checking, and data manipulation.
$.each() - Iteration
// Array iteration
var colors = ['red', 'green', 'blue'];
$.each(colors, function(index, value) {
console.log(index + ': ' + value);
});
// 0: red, 1: green, 2: blue
// Object iteration
var person = { name: 'John', age: 30, city: 'NYC' };
$.each(person, function(key, value) {
console.log(key + ': ' + value);
});
// name: John, age: 30, city: NYC
// DOM elements iteration
$.each($('li'), function(index, element) {
$(element).text('Item ' + (index + 1));
});
// Early exit
$.each([1, 2, 3, 4, 5], function(index, value) {
if (value === 3) {
return false; // Break loop
}
console.log(value);
});
$.map() - Transform Arrays
// Map array
var numbers = [1, 2, 3, 4, 5];
var doubled = $.map(numbers, function(value, index) {
return value * 2;
});
console.log(doubled); // [2, 4, 6, 8, 10]
// Map object
var user = { firstName: 'John', lastName: 'Doe' };
var values = $.map(user, function(value, key) {
return value;
});
console.log(values); // ['John', 'Doe']
// Filter while mapping
var numbers = [1, 2, 3, 4, 5];
var evenDoubled = $.map(numbers, function(value) {
return value % 2 === 0 ? value * 2 : null;
});
console.log(evenDoubled); // [4, 8]
// Map DOM elements
var texts = $.map($('li'), function(element) {
return $(element).text();
});
$.grep() - Filter Arrays
// Basic filtering
var numbers = [1, 2, 3, 4, 5, 6];
var evenNumbers = $.grep(numbers, function(value) {
return value % 2 === 0;
});
console.log(evenNumbers); // [2, 4, 6]
// Invert filter (get odd numbers)
var oddNumbers = $.grep(numbers, function(value) {
return value % 2 === 0;
}, true); // true inverts the filter
console.log(oddNumbers); // [1, 3, 5]
// Filter objects
var users = [
{ name: 'John', age: 25 },
{ name: 'Jane', age: 30 },
{ name: 'Bob', age: 20 }
];
var adults = $.grep(users, function(user) {
return user.age >= 25;
});
console.log(adults); // [{name: 'John', age: 25}, {name: 'Jane', age: 30}]
$.extend() - Merge Objects
// Shallow merge
var defaults = { color: 'red', size: 'medium' };
var options = { size: 'large', weight: 'bold' };
var settings = $.extend({}, defaults, options);
console.log(settings);
// { color: 'red', size: 'large', weight: 'bold' }
// Deep merge
var obj1 = { data: { name: 'John', age: 30 } };
var obj2 = { data: { age: 25, city: 'NYC' } };
var merged = $.extend(true, {}, obj1, obj2);
console.log(merged);
// { data: { name: 'John', age: 25, city: 'NYC' } }
// Extend existing object
var config = { debug: false };
$.extend(config, { debug: true, verbose: true });
console.log(config); // { debug: true, verbose: true }
// Plugin pattern
$.fn.myPlugin = function(options) {
var settings = $.extend({
color: 'blue',
size: 10
}, options);
// Use settings...
};
Type Checking
// Type checks
$.isArray([1, 2, 3]); // true
$.isArray('text'); // false
$.isFunction(function() {}); // true
$.isFunction('text'); // false
$.isNumeric(123); // true
$.isNumeric('123'); // true
$.isNumeric('abc'); // false
$.isEmptyObject({}); // true
$.isEmptyObject({a: 1}); // false
$.isPlainObject({}); // true
$.isPlainObject(new Date()); // false
$.type(true); // 'boolean'
$.type(3); // 'number'
$.type('test'); // 'string'
$.type([]); // 'array'
$.type({}); // 'object'
$.type(null); // 'null'
$.type(undefined); // 'undefined'
// Example usage
function processData(data) {
if ($.isArray(data)) {
return data.length;
} else if ($.isPlainObject(data)) {
return Object.keys(data).length;
}
return 0;
}
$.inArray() - Find in Array
// Find index of value
var colors = ['red', 'green', 'blue'];
var index = $.inArray('green', colors);
console.log(index); // 1
var notFound = $.inArray('yellow', colors);
console.log(notFound); // -1 (not found)
// With starting index
var numbers = [1, 2, 3, 2, 5];
var index = $.inArray(2, numbers, 2); // Start at index 2
console.log(index); // 3
// Check if exists
if ($.inArray('blue', colors) !== -1) {
console.log('Blue exists');
}
// Example: Check selected values
var selectedIds = [1, 3, 5];
$('.item').each(function() {
var id = $(this).data('id');
if ($.inArray(id, selectedIds) !== -1) {
$(this).addClass('selected');
}
});
$.trim() - Remove Whitespace
// Trim whitespace
var text = ' Hello World ';
var trimmed = $.trim(text);
console.log(trimmed); // 'Hello World'
// Trim form inputs
var name = $.trim($('#name').val());
if (name === '') {
alert('Name is required');
}
// Trim all inputs in form
$('input[type="text"]').each(function() {
$(this).val($.trim($(this).val()));
});
// Example: Clean data
var userData = {
name: $.trim($('#name').val()),
email: $.trim($('#email').val()),
message: $.trim($('#message').val())
};
$.parseJSON() and $.parseHTML()
// Parse JSON
var jsonString = '{"name":"John","age":30}';
var obj = $.parseJSON(jsonString);
console.log(obj.name); // 'John'
// Modern alternative: JSON.parse()
var obj2 = JSON.parse(jsonString);
// Parse HTML
var htmlString = '<div><p>Hello</p></div>';
var $parsed = $.parseHTML(htmlString);
$('#container').append($parsed);
// Parse with script filtering
var html = '<div>Content</div><script>alert("XSS")</script>';
var safe = $.parseHTML(html, document, false); // false = no scripts
// Parse XML
var xmlString = '<root><item>Value</item></root>';
var xml = $.parseXML(xmlString);
var $xml = $(xml);
var value = $xml.find('item').text();
$.makeArray() - Convert to Array
// Convert jQuery object to array
var $items = $('li');
var itemsArray = $.makeArray($items);
// Convert NodeList to array
var divs = document.querySelectorAll('div');
var divsArray = $.makeArray(divs);
// Work with array methods
var texts = $.makeArray($('p')).map(function(p) {
return p.textContent;
});
// Modern alternative
var modernArray = Array.from($('li'));
$.proxy() - Bind Context
// Preserve context
var obj = {
name: 'MyObject',
logName: function() {
console.log(this.name);
}
};
// Without proxy - 'this' is button
$('#btn').click(obj.logName); // undefined
// With proxy - 'this' is obj
$('#btn').click($.proxy(obj.logName, obj)); // 'MyObject'
// Alternative syntax
$('#btn').click($.proxy(obj, 'logName'));
// With additional arguments
var handler = $.proxy(function(event, extra) {
console.log(this.name, extra);
}, obj, 'additional');
// Modern alternative: bind()
$('#btn').click(obj.logName.bind(obj));
Practice Tasks
- Use $.each() to iterate over an array of objects.
- Filter an array with $.grep() to find matching items.
- Merge two configuration objects with $.extend().
- Validate form data using type checking utilities.
Key Takeaways
- $.each() iterates arrays and objects.
- $.map() transforms arrays, $.grep() filters them.
- $.extend() merges objects (use true for deep merge).
- Type checking: $.isArray(), $.isFunction(), $.isNumeric().
- $.trim() removes whitespace from strings.
What's Next?
Next Topic: Learn Data Storage.
jQuery Plugins: Extend Functionality
Create reusable jQuery components and use popular plugins
What are jQuery Plugins?
Plugins extend jQuery's functionality, allowing you to create reusable, chainable methods for custom behaviors.
Creating a Basic Plugin
// Basic plugin template
$.fn.myPlugin = function() {
// 'this' is the jQuery object
return this.each(function() {
// 'this' is the DOM element
$(this).css('background', 'yellow');
});
};
// Usage
$('.box').myPlugin();
// With options
$.fn.highlight = function(options) {
var settings = $.extend({
color: 'yellow',
duration: 1000
}, options);
return this.each(function() {
$(this).css('background-color', settings.color)
.delay(settings.duration)
.queue(function(next) {
$(this).css('background-color', '');
next();
});
});
};
// Usage
$('.box').highlight({ color: 'red', duration: 2000 });
$('.box').highlight(); // Uses defaults
Plugin Best Practices
// Proper plugin structure
;(function($) {
$.fn.accordion = function(options) {
// Default settings
var settings = $.extend({
duration: 300,
activeClass: 'active',
headerSelector: '.accordion-header',
contentSelector: '.accordion-content'
}, options);
// Return for chaining
return this.each(function() {
var $accordion = $(this);
var $headers = $accordion.find(settings.headerSelector);
var $contents = $accordion.find(settings.contentSelector);
// Hide all content initially
$contents.hide();
// Click handler
$headers.on('click.accordion', function() {
var $header = $(this);
var $content = $header.next(settings.contentSelector);
// Close others
$headers.not($header).removeClass(settings.activeClass);
$contents.not($content).slideUp(settings.duration);
// Toggle current
$header.toggleClass(settings.activeClass);
$content.slideToggle(settings.duration);
});
});
};
// Add method
$.fn.accordion.destroy = function() {
return this.each(function() {
$(this).find('.accordion-header').off('.accordion');
});
};
})(jQuery);
// Usage
$('.my-accordion').accordion({
duration: 500,
activeClass: 'open'
});
Chainable Plugins
// Always return 'this'
$.fn.makeRed = function() {
return this.css('color', 'red');
};
$.fn.makeBold = function() {
return this.css('font-weight', 'bold');
};
// Chain plugin methods
$('.text')
.makeRed()
.makeBold()
.fadeIn();
// Plugin with getter/setter
$.fn.customData = function(key, value) {
// Getter
if (value === undefined) {
return this.data('custom-' + key);
}
// Setter (chainable)
return this.each(function() {
$(this).data('custom-' + key, value);
});
};
// Usage
$('#box').customData('count', 5); // Setter
var count = $('#box').customData('count'); // Getter
Plugin with Public Methods
;(function($) {
var methods = {
init: function(options) {
var settings = $.extend({
color: 'blue',
size: 'medium'
}, options);
return this.each(function() {
var $this = $(this);
$this.data('myWidget', settings);
$this.css('color', settings.color);
});
},
show: function() {
return this.each(function() {
$(this).fadeIn();
});
},
hide: function() {
return this.each(function() {
$(this).fadeOut();
});
},
destroy: function() {
return this.each(function() {
$(this).removeData('myWidget');
});
}
};
$.fn.myWidget = function(method) {
if (methods[method]) {
return methods[method].apply(this,
Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist');
}
};
})(jQuery);
// Usage
$('#element').myWidget({ color: 'red' }); // Init
$('#element').myWidget('show'); // Call method
$('#element').myWidget('hide');
$('#element').myWidget('destroy');
Popular jQuery Plugins
// Slick Carousel
$('.carousel').slick({
dots: true,
infinite: true,
speed: 500,
slidesToShow: 3,
slidesToScroll: 1
});
// Select2 (enhanced select)
$('#country').select2({
placeholder: 'Select a country',
allowClear: true
});
// DataTables
$('#myTable').DataTable({
paging: true,
searching: true,
ordering: true
});
// jQuery Validation
$('#form').validate({
rules: {
email: {
required: true,
email: true
},
password: {
required: true,
minlength: 8
}
},
messages: {
email: 'Please enter a valid email',
password: 'Password must be at least 8 characters'
}
});
// Magnific Popup
$('.image-link').magnificPopup({
type: 'image',
gallery: {
enabled: true
}
});
// Toastr (notifications)
toastr.success('Saved successfully!');
toastr.error('An error occurred');
Complete Plugin Example
;(function($) {
'use strict';
$.fn.tooltip = function(options) {
var settings = $.extend({
position: 'top',
delay: 200,
content: null,
className: 'tooltip'
}, options);
return this.each(function() {
var $element = $(this);
var $tooltip;
var timer;
// Create tooltip
function createTooltip() {
var content = settings.content || $element.attr('title');
$tooltip = $('<div>')
.addClass(settings.className)
.text(content)
.appendTo('body')
.hide();
}
// Position tooltip
function positionTooltip() {
var offset = $element.offset();
var width = $element.outerWidth();
var height = $element.outerHeight();
var tooltipWidth = $tooltip.outerWidth();
var tooltipHeight = $tooltip.outerHeight();
var left, top;
switch(settings.position) {
case 'top':
left = offset.left + (width / 2) - (tooltipWidth / 2);
top = offset.top - tooltipHeight - 10;
break;
case 'bottom':
left = offset.left + (width / 2) - (tooltipWidth / 2);
top = offset.top + height + 10;
break;
case 'left':
left = offset.left - tooltipWidth - 10;
top = offset.top + (height / 2) - (tooltipHeight / 2);
break;
case 'right':
left = offset.left + width + 10;
top = offset.top + (height / 2) - (tooltipHeight / 2);
break;
}
$tooltip.css({ left: left, top: top });
}
// Show tooltip
function showTooltip() {
clearTimeout(timer);
timer = setTimeout(function() {
createTooltip();
positionTooltip();
$tooltip.fadeIn(200);
}, settings.delay);
}
// Hide tooltip
function hideTooltip() {
clearTimeout(timer);
if ($tooltip) {
$tooltip.fadeOut(200, function() {
$(this).remove();
});
}
}
// Bind events
$element
.on('mouseenter.tooltip', showTooltip)
.on('mouseleave.tooltip', hideTooltip);
// Remove title to prevent default tooltip
$element.removeAttr('title');
});
};
})(jQuery);
// Usage
$('.has-tooltip').tooltip({
position: 'top',
delay: 300
});
Plugin Testing
// Test plugin initialization
QUnit.test('Plugin initializes', function(assert) {
var $element = $('<div>');
$element.myPlugin();
assert.ok($element.data('myPlugin'), 'Plugin data stored');
});
// Test options
QUnit.test('Plugin accepts options', function(assert) {
var $element = $('<div>');
$element.myPlugin({ color: 'red' });
var settings = $element.data('myPlugin');
assert.equal(settings.color, 'red', 'Option applied');
});
// Test chainability
QUnit.test('Plugin is chainable', function(assert) {
var $element = $('<div>');
var $result = $element.myPlugin().addClass('test');
assert.ok($result.hasClass('test'), 'Chaining works');
});
// Manual testing
console.log('Testing plugin...');
$('.test').myPlugin({ option: 'value' });
console.log('Plugin initialized');
Practice Tasks
- Create a simple plugin that changes text color.
- Build a plugin with configurable options and defaults.
- Implement a toggle plugin with public methods.
- Create a modal dialog plugin.
Key Takeaways
- Plugins extend $.fn to add custom methods.
- Always return 'this' for chainability.
- Use $.extend() to merge options with defaults.
- Wrap plugins in IIFE to protect $ variable.
- Namespace events to prevent conflicts.
What's Next?
Next Topic: Learn Migration and Modernization.
jQuery UI: Rich Interface Components
Build interactive user interfaces with jQuery UI widgets
What is jQuery UI?
jQuery UI is an official jQuery library providing draggable, droppable, resizable, sortable, and many other UI widgets and interactions.
Setup jQuery UI
<!-- jQuery (required first) -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- jQuery UI JavaScript -->
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
<!-- jQuery UI CSS (theme) -->
<link rel="stylesheet"
href="https://code.jquery.com/ui/1.13.2/themes/smoothness/jquery-ui.css">
// Verify jQuery UI loaded
if (typeof $.ui !== 'undefined') {
console.log('jQuery UI version:', $.ui.version);
} else {
console.error('jQuery UI not loaded');
}
Draggable
// Basic draggable
$('#box').draggable();
// With options
$('#box').draggable({
axis: 'x', // Only horizontal
containment: 'parent', // Keep inside parent
cursor: 'move',
opacity: 0.7,
revert: true, // Snap back after drag
snap: '.grid', // Snap to grid elements
grid: [20, 20] // Snap to 20px grid
});
// Events
$('#box').draggable({
start: function(event, ui) {
console.log('Drag started');
},
drag: function(event, ui) {
console.log('Dragging:', ui.position);
},
stop: function(event, ui) {
console.log('Drag stopped at:', ui.position);
}
});
// Method calls
$('#box').draggable('disable'); // Disable dragging
$('#box').draggable('enable'); // Enable dragging
$('#box').draggable('destroy'); // Remove draggable
Droppable
// Make elements draggable and droppable
$('.drag-item').draggable();
$('#dropzone').droppable({
accept: '.drag-item', // Only accept these
drop: function(event, ui) {
var $item = ui.draggable;
console.log('Dropped:', $item.text());
$(this).append($item);
$(this).addClass('has-drop');
},
over: function(event, ui) {
$(this).addClass('highlight');
},
out: function(event, ui) {
$(this).removeClass('highlight');
}
});
// Example: Drag and drop game
$('.word').draggable({ revert: 'invalid' });
$('.sentence-slot').droppable({
accept: '.word',
drop: function(event, ui) {
var $word = ui.draggable;
$(this).text($word.text()).data('filled', true);
$word.hide();
checkComplete();
}
});
function checkComplete() {
if ($('.sentence-slot').length ===
$('.sentence-slot[data-filled]').length) {
alert('Sentence complete!');
}
}
Sortable
// Make list sortable
$('#sortable-list').sortable();
// With options
$('#sortable-list').sortable({
placeholder: 'ui-state-highlight',
cursor: 'move',
opacity: 0.6,
axis: 'y', // Only vertical sorting
update: function(event, ui) {
console.log('Order changed');
var order = $(this).sortable('toArray');
console.log('New order:', order);
}
});
// Connect sortable lists
$('#list1, #list2').sortable({
connectWith: '.connected-sortable'
});
// Get order
var order = $('#sortable-list').sortable('toArray');
console.log('Current order:', order);
// Serialize for form submission
var serialized = $('#sortable-list').sortable('serialize');
console.log(serialized); // id[]=1&id[]=2&id[]=3
Resizable
// Basic resizable
$('#box').resizable();
// With constraints
$('#box').resizable({
minWidth: 100,
maxWidth: 500,
minHeight: 100,
maxHeight: 500,
aspectRatio: true, // Maintain aspect ratio
handles: 'e, s, se' // Only right, bottom, corner
});
// Events
$('#box').resizable({
start: function(event, ui) {
console.log('Resize started');
},
resize: function(event, ui) {
console.log('Size:', ui.size);
},
stop: function(event, ui) {
console.log('Final size:', ui.size);
}
});
// Resizable within container
$('#box').resizable({
containment: 'parent'
});
Datepicker
// Basic datepicker
$('#date').datepicker();
// With options
$('#date').datepicker({
dateFormat: 'yy-mm-dd',
minDate: 0, // Today onwards
maxDate: '+1Y', // Up to 1 year ahead
numberOfMonths: 2, // Show 2 months
showButtonPanel: true,
changeMonth: true,
changeYear: true,
yearRange: '1900:2100'
});
// Events
$('#date').datepicker({
onSelect: function(dateText, inst) {
console.log('Selected:', dateText);
},
beforeShowDay: function(date) {
// Disable weekends
var day = date.getDay();
return [(day !== 0 && day !== 6), ''];
}
});
// Get/set date
var date = $('#date').datepicker('getDate');
$('#date').datepicker('setDate', '+7'); // 7 days from today
// Date range
$('#from').datepicker({
onSelect: function(selectedDate) {
$('#to').datepicker('option', 'minDate', selectedDate);
}
});
$('#to').datepicker({
onSelect: function(selectedDate) {
$('#from').datepicker('option', 'maxDate', selectedDate);
}
});
Dialog
// Basic dialog
$('#dialog').dialog();
// With options
$('#dialog').dialog({
title: 'My Dialog',
width: 400,
height: 300,
modal: true,
resizable: false,
draggable: true,
buttons: {
'OK': function() {
console.log('OK clicked');
$(this).dialog('close');
},
'Cancel': function() {
$(this).dialog('close');
}
},
close: function() {
console.log('Dialog closed');
}
});
// Confirmation dialog
function confirm(message, callback) {
$('<div>').html(message).dialog({
title: 'Confirm',
modal: true,
buttons: {
'Yes': function() {
callback(true);
$(this).dialog('close');
},
'No': function() {
callback(false);
$(this).dialog('close');
}
}
});
}
// Usage
confirm('Delete this item?', function(result) {
if (result) {
console.log('Item deleted');
}
});
// Open/close dialog
$('#dialog').dialog('open');
$('#dialog').dialog('close');
Accordion
// Basic accordion
$('#accordion').accordion();
// With options
$('#accordion').accordion({
collapsible: true, // Allow all closed
heightStyle: 'content', // Auto height
active: 0, // First panel open
animate: 300,
event: 'click'
});
// Events
$('#accordion').accordion({
activate: function(event, ui) {
console.log('Activated:', ui.newHeader.text());
}
});
// Control accordion
$('#accordion').accordion('option', 'active', 2); // Open 3rd panel
var active = $('#accordion').accordion('option', 'active');
console.log('Active panel:', active);
Tabs
// Basic tabs
$('#tabs').tabs();
// With options
$('#tabs').tabs({
active: 0, // First tab active
collapsible: false,
event: 'mouseover', // Change on hover
activate: function(event, ui) {
console.log('Tab changed to:', ui.newTab.index());
}
});
// Switch tabs
$('#tabs').tabs('option', 'active', 2); // Switch to 3rd tab
// Add/remove tabs
$('#tabs').tabs('add', '#new-tab', 'New Tab');
$('#tabs').tabs('remove', 2);
// Enable/disable tabs
$('#tabs').tabs('enable', 1);
$('#tabs').tabs('disable', 2);
// Load tab content via Ajax
$('#tabs').tabs({
beforeLoad: function(event, ui) {
ui.jqXHR.fail(function() {
ui.panel.html('Failed to load content');
});
}
});
Progressbar and Slider
// Progressbar
$('#progressbar').progressbar({
value: 37
});
// Update progress
$('#progressbar').progressbar('value', 75);
// Animate progress
var progress = 0;
var interval = setInterval(function() {
progress += 10;
$('#progressbar').progressbar('value', progress);
if (progress >= 100) {
clearInterval(interval);
}
}, 500);
// Slider
$('#slider').slider({
min: 0,
max: 100,
value: 50,
slide: function(event, ui) {
$('#amount').text(ui.value);
},
change: function(event, ui) {
console.log('Changed to:', ui.value);
}
});
// Range slider
$('#range-slider').slider({
range: true,
min: 0,
max: 500,
values: [100, 300],
slide: function(event, ui) {
$('#range').text('$' + ui.values[0] + ' - $' + ui.values[1]);
}
});
Autocomplete
// Local data source
var tags = ['JavaScript', 'jQuery', 'HTML', 'CSS', 'PHP'];
$('#search').autocomplete({
source: tags,
minLength: 1
});
// Ajax data source
$('#search').autocomplete({
source: '/api/search',
minLength: 2,
select: function(event, ui) {
console.log('Selected:', ui.item.value);
}
});
// Custom rendering
$('#search').autocomplete({
source: function(request, response) {
$.get('/api/search', { q: request.term }, function(data) {
response(data);
});
},
select: function(event, ui) {
console.log('Selected:', ui.item);
}
}).data('ui-autocomplete')._renderItem = function(ul, item) {
return $('<li>')
.append('<div>' + item.label + ' (' + item.category + ')</div>')
.appendTo(ul);
};
Practice Tasks
- Create a draggable and resizable box.
- Build a sortable to-do list.
- Implement a date picker for form input.
- Create a tabbed interface with multiple panels.
Key Takeaways
- jQuery UI provides ready-to-use UI widgets and interactions.
- Includes draggable, droppable, sortable, resizable, and more.
- Widgets like datepicker, dialog, accordion, tabs simplify UI development.
- All components are themeable and customizable.
- Call methods using widget('method', args) syntax.
What's Next?
Next Topic: Explore Best Practices.
jQuery Mobile: Mobile-Optimized Framework
Build touch-friendly mobile web applications
What is jQuery Mobile?
jQuery Mobile is a framework for creating responsive, touch-optimized websites and apps for smartphones and tablets.
Setup jQuery Mobile
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- jQuery Mobile CSS -->
<link rel="stylesheet"
href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<!-- jQuery Mobile JS -->
<script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
<!-- Content -->
</body>
</html>
Page Structure
<div data-role="page" id="home">
<div data-role="header">
<h1>My App</h1>
</div>
<div data-role="content">
<p>Welcome to my mobile app!</p>
<a href="#about" data-role="button">About</a>
</div>
<div data-role="footer" data-position="fixed">
<h4>Copyright 2024</h4>
</div>
</div>
Multiple Pages
<!-- Page 1 -->
<div data-role="page" id="page1">
<div data-role="header">
<h1>Page 1</h1>
</div>
<div data-role="content">
<a href="#page2" data-role="button">Go to Page 2</a>
</div>
</div>
<!-- Page 2 -->
<div data-role="page" id="page2">
<div data-role="header" data-add-back-btn="true">
<h1>Page 2</h1>
</div>
<div data-role="content">
<a href="#page1" data-role="button">Back to Page 1</a>
</div>
</div>
// Page events
$(document).on('pagebeforeshow', '#page2', function() {
console.log('Page 2 about to show');
});
$(document).on('pageshow', '#page2', function() {
console.log('Page 2 visible');
});
$(document).on('pagehide', '#page1', function() {
console.log('Page 1 hidden');
});
// Change page programmatically
$.mobile.changePage('#page2', {
transition: 'slide',
reverse: false
});
Mobile Buttons
<!-- Basic button -->
<a href="#" data-role="button">Click Me</a>
<!-- Button with icon -->
<a href="#" data-role="button" data-icon="star">Favorite</a>
<!-- Icon position -->
<a href="#" data-role="button" data-icon="home"
data-iconpos="right">Home</a>
<!-- Icon only -->
<a href="#" data-role="button" data-icon="delete"
data-iconpos="notext">Delete</a>
<!-- Inline buttons -->
<a href="#" data-role="button" data-inline="true">Save</a>
<a href="#" data-role="button" data-inline="true">Cancel</a>
<!-- Button group -->
<div data-role="controlgroup" data-type="horizontal">
<a href="#" data-role="button">Yes</a>
<a href="#" data-role="button">No</a>
<a href="#" data-role="button">Maybe</a>
</div>
Lists and Listviews
<ul data-role="listview">
<li><a href="#">Item 1</a></li>
<li><a href="#">Item 2</a></li>
<li><a href="#">Item 3</a></li>
</ul>
<!-- List with icons -->
<ul data-role="listview" data-inset="true">
<li data-icon="delete">
<a href="#">Delete Item</a>
</li>
<li data-icon="plus">
<a href="#">Add Item</a>
</li>
</ul>
<!-- List with split buttons -->
<ul data-role="listview">
<li>
<a href="#">
<h2>John Doe</h2>
<p>Software Developer</p>
</a>
<a href="#" data-icon="gear">Options</a>
</li>
</ul>
<!-- Searchable list -->
<ul data-role="listview" data-filter="true"
data-filter-placeholder="Search...">
<li><a href="#">Apple</a></li>
<li><a href="#">Banana</a></li>
<li><a href="#">Cherry</a></li>
</ul>
Mobile Forms
<form>
<!-- Text input -->
<label for="name">Name:</label>
<input type="text" name="name" id="name"
placeholder="Enter your name">
<!-- Select menu -->
<label for="select">Choose:</label>
<select name="select" id="select">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
<!-- Slider -->
<label for="slider">Volume:</label>
<input type="range" name="slider" id="slider"
value="50" min="0" max="100">
<!-- Flip toggle switch -->
<label for="flip">Notifications:</label>
<select name="flip" id="flip" data-role="slider">
<option value="off">Off</option>
<option value="on">On</option>
</select>
<!-- Checkboxes -->
<fieldset data-role="controlgroup">
<legend>Choose:</legend>
<input type="checkbox" name="cb1" id="cb1">
<label for="cb1">Option 1</label>
<input type="checkbox" name="cb2" id="cb2">
<label for="cb2">Option 2</label>
</fieldset>
<button type="submit" data-role="button">Submit</button>
</form>
Popups and Panels
<!-- Popup -->
<a href="#popup" data-rel="popup" data-role="button">Open Popup</a>
<div data-role="popup" id="popup">
<p>This is a popup!</p>
<a href="#" data-rel="back" data-role="button">Close</a>
</div>
<!-- Panel (sidebar) -->
<div data-role="panel" id="menu" data-position="left"
data-display="overlay">
<h2>Menu</h2>
<ul data-role="listview">
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
</ul>
</div>
<a href="#menu" data-role="button">Open Menu</a>
// Open/close popup
$('#popup').popup('open');
$('#popup').popup('close');
// Panel events
$(document).on('panelopen', '#menu', function() {
console.log('Menu opened');
});
Touch Events
// Tap
$('#element').on('tap', function() {
console.log('Tapped');
});
// Tap and hold
$('#element').on('taphold', function() {
console.log('Tap and hold');
});
// Swipe
$('#element').on('swipeleft', function() {
console.log('Swiped left');
});
$('#element').on('swiperight', function() {
console.log('Swiped right');
});
// Custom swipe handling
$('#element').on('swipe', function(e) {
console.log('Swipe detected');
});
// Orientation change
$(window).on('orientationchange', function(event) {
if (event.orientation === 'portrait') {
console.log('Portrait mode');
} else {
console.log('Landscape mode');
}
});
Themes and Styling
<!-- Apply theme swatches -->
<div data-role="page" data-theme="a">
<div data-role="header" data-theme="b">
<h1>Header</h1>
</div>
<div data-role="content" data-theme="c">
<p>Content</p>
</div>
</div>
<!-- Button themes -->
<a href="#" data-role="button" data-theme="a">Theme A</a>
<a href="#" data-role="button" data-theme="b">Theme B</a>
Configuration
// Configure before jQuery Mobile loads
$(document).on('mobileinit', function() {
// Disable page loading message
$.mobile.loadingMessage = false;
// Change default transition
$.mobile.defaultPageTransition = 'slide';
// Change page loading theme
$.mobile.pageLoadErrorMessageTheme = 'e';
// Disable Ajax navigation
$.mobile.ajaxEnabled = false;
// Change tap hold threshold
$.event.special.tap.tapholdThreshold = 750;
});
Practice Tasks
- Create a multi-page mobile app with navigation.
- Build a touch-friendly list with search filter.
- Implement swipe gestures for navigation.
- Create a mobile form with various input types.
Key Takeaways
- jQuery Mobile optimizes jQuery for mobile devices.
- Uses data-role attributes for easy component creation.
- Provides touch events: tap, swipe, taphold.
- Includes mobile-optimized widgets: lists, buttons, forms.
- Supports page transitions and Ajax navigation.
- Note: jQuery Mobile is now in maintenance mode; consider modern alternatives.
What's Next?
Final Topics: Review Best Practices and Migration strategies.
Performance Optimization
Make jQuery code faster and more efficient
Why Performance Matters
Optimized jQuery code loads faster, uses less memory, and provides better user experience, especially on mobile devices.
Cache Selectors
// BAD: Repeated selections
$('#box').addClass('active');
$('#box').fadeIn();
$('#box').css('color', 'red');
// GOOD: Cache selection
var $box = $('#box');
$box.addClass('active');
$box.fadeIn();
$box.css('color', 'red');
// BETTER: Chain methods
$('#box')
.addClass('active')
.fadeIn()
.css('color', 'red');
// Cache in loops
// BAD
for (var i = 0; i < 100; i++) {
$('.container').append('<div>' + i + '</div>');
}
// GOOD
var $container = $('.container');
for (var i = 0; i < 100; i++) {
$container.append('<div>' + i + '</div>');
}
Efficient Selectors
// Fastest to slowest selectors:
// 1. ID selector (fastest)
$('#myId');
// 2. Element selector
$('div');
// 3. Class selector
$('.myClass');
// 4. Attribute selector
$('[data-id="123"]');
// 5. Pseudo selector (slowest)
$(':hidden');
// Be specific - provide context
// BAD: Searches entire document
$('.item');
// GOOD: Limit search scope
$('#container .item');
$('.item', '#container'); // Same as above
// Use find() for descendants
var $container = $('#container');
$container.find('.item'); // Faster
// Avoid universal selector
// BAD
$('div *');
// GOOD: Be specific
$('div > p');
Minimize DOM Manipulation
// BAD: Multiple manipulations
for (var i = 0; i < 100; i++) {
$('#list').append('<li>Item ' + i + '</li>');
}
// 100 DOM manipulations!
// GOOD: Build string, append once
var html = '';
for (var i = 0; i < 100; i++) {
html += '<li>Item ' + i + '</li>';
}
$('#list').html(html);
// 1 DOM manipulation!
// BETTER: Use array and join
var items = [];
for (var i = 0; i < 100; i++) {
items.push('<li>Item ' + i + '</li>');
}
$('#list').html(items.join(''));
// Detach, modify, reattach
var $list = $('#list').detach();
for (var i = 0; i < 100; i++) {
$list.append('<li>Item ' + i + '</li>');
}
$('body').append($list);
// Use DocumentFragment
var fragment = document.createDocumentFragment();
for (var i = 0; i < 100; i++) {
var li = document.createElement('li');
li.textContent = 'Item ' + i;
fragment.appendChild(li);
}
$('#list')[0].appendChild(fragment);
Event Delegation
// BAD: Attach handler to each item
$('.item').click(function() {
$(this).addClass('selected');
});
// 100 items = 100 event handlers
// GOOD: One delegated handler
$('#container').on('click', '.item', function() {
$(this).addClass('selected');
});
// 1 event handler for all items
// Works for dynamic elements too
$('#list').on('click', 'li', function() {
// Works for li added later
});
// Delegate to closest static parent
// BAD
$(document).on('click', '.button', handler);
// GOOD
$('.button-container').on('click', '.button', handler);
Debounce and Throttle
// Debounce - wait until user stops
function debounce(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
};
}
// Usage: Search after typing stops
var search = debounce(function() {
var query = $('#search').val();
// Perform search
}, 300);
$('#search').on('keyup', search);
// Throttle - limit frequency
function throttle(func, limit) {
var waiting = false;
return function() {
if (!waiting) {
func.apply(this, arguments);
waiting = true;
setTimeout(function() {
waiting = false;
}, limit);
}
};
}
// Usage: Scroll event
var handleScroll = throttle(function() {
console.log('Scrolled');
}, 100);
$(window).on('scroll', handleScroll);
Use Native JavaScript When Possible
// Native is faster
// Get element by ID
// jQuery
var element = $('#myId');
// Native (faster)
var element = document.getElementById('myId');
// Get elements by class
// jQuery
var elements = $('.myClass');
// Native (faster)
var elements = document.getElementsByClassName('myClass');
// Query selector
// jQuery
var element = $('.container .item');
// Native (faster)
var element = document.querySelector('.container .item');
// Loop
// jQuery
$('.item').each(function() {
// ...
});
// Native (faster)
var items = document.querySelectorAll('.item');
for (var i = 0; i < items.length; i++) {
// ...
}
// Add class
// jQuery
$('#box').addClass('active');
// Native (faster)
document.getElementById('box').classList.add('active');
// When to use native:
// - Simple operations
// - Performance critical code
// - Modern browsers only
// When to use jQuery:
// - Complex operations
// - Cross-browser compatibility
// - Chaining multiple operations
Optimize Animations
// Use CSS transitions when possible
// jQuery animation
$('#box').animate({ left: '100px' }, 500);
// CSS transition (faster)
$('#box').css('left', '100px'); // CSS handles transition
// CSS:
// #box { transition: left 0.5s; }
// Avoid animating layout properties
// BAD: Causes reflow
$('#box').animate({
width: '200px',
height: '200px',
marginLeft: '50px'
}, 500);
// GOOD: Use transform (GPU accelerated)
$('#box').css('transform', 'scale(1.2) translateX(50px)');
// Stop animations before starting new ones
$('#box').stop(true, true).fadeIn();
// Use requestAnimationFrame for custom animations
function animate() {
// Animation logic
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Memory Management
// Remove event handlers
$('#element').off('click');
$('#element').off(); // Remove all events
// Remove data
$('#element').removeData();
// Remove elements properly
$('#element').remove(); // Removes element and data
// vs
$('#element').empty(); // Only empties content
// Clear intervals/timeouts
var interval = setInterval(function() {
// ...
}, 1000);
clearInterval(interval);
// Avoid circular references
// BAD
var element = document.getElementById('box');
element.data = { element: element }; // Circular
// Use weak references or clear when done
element.data = null;
// Undelegate events when done
$(document).off('.myNamespace');
Lazy Loading
// Lazy load images
$(window).on('scroll', function() {
$('.lazy-image').each(function() {
var $img = $(this);
if (isInViewport($img)) {
$img.attr('src', $img.data('src'));
$img.removeClass('lazy-image');
}
});
});
function isInViewport($element) {
var elementTop = $element.offset().top;
var elementBottom = elementTop + $element.outerHeight();
var viewportTop = $(window).scrollTop();
var viewportBottom = viewportTop + $(window).height();
return elementBottom > viewportTop && elementTop < viewportBottom;
}
// Lazy load content
$('.load-more').click(function() {
$.get('/api/more', function(html) {
$('#content').append(html);
});
});
// Infinite scroll
$(window).on('scroll', throttle(function() {
if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) {
loadMoreContent();
}
}, 200));
Measurement and Profiling
// Measure execution time
console.time('Operation');
// Code to measure
$('.item').each(function() {
$(this).addClass('active');
});
console.timeEnd('Operation');
// Compare methods
console.time('Method 1');
$('.item').addClass('active');
console.timeEnd('Method 1');
console.time('Method 2');
$('.item').each(function() {
this.className += ' active';
});
console.timeEnd('Method 2');
// Profile with browser DevTools
// Chrome: Performance tab
// Record → Perform action → Stop
// Mark performance points
performance.mark('start');
// Operations
performance.mark('end');
performance.measure('Operation', 'start', 'end');
console.log(performance.getEntriesByName('Operation'));
Practice Tasks
- Cache a jQuery selector that's used multiple times.
- Optimize a loop that manipulates the DOM.
- Convert direct event binding to event delegation.
- Measure performance before and after optimization.
Key Takeaways
- Cache jQuery selectors to avoid repeated DOM queries.
- Use event delegation instead of binding to each element.
- Batch DOM manipulations to minimize reflows.
- Debounce/throttle high-frequency events like scroll.
- Use native JavaScript for simple, performance-critical operations.
- Prefer CSS transitions over jQuery animations when possible.
What's Next?
Next Topic: Learn Mobile jQuery.
Chaining: Elegant Code
Link multiple jQuery methods for cleaner syntax
What is Method Chaining?
Chaining allows you to call multiple jQuery methods on the same element in a single statement, making code more readable and efficient.
Basic Chaining
// Without chaining - repetitive
$('#box').css('color', 'red');
$('#box').fadeIn();
$('#box').addClass('active');
// With chaining - clean and efficient
$('#box')
.css('color', 'red')
.fadeIn()
.addClass('active');
// Multiple CSS properties
$('#box')
.css({
color: 'red',
fontSize: '20px',
backgroundColor: 'yellow'
})
.fadeIn(500)
.addClass('highlight');
// Chaining DOM manipulation
$('<div>')
.addClass('container')
.html('<p>Hello World</p>')
.appendTo('#main');
Chaining with Traversal
// Chain traversal methods
$('li:first')
.css('font-weight', 'bold')
.next()
.css('color', 'red')
.next()
.css('color', 'blue');
// Parent and children
$('.child')
.addClass('active')
.parent()
.addClass('parent-active')
.siblings()
.addClass('sibling-highlight');
// Find and filter
$('#container')
.find('p')
.filter('.intro')
.css('font-style', 'italic')
.end() // Back to all p
.css('margin', '10px');
// Complex traversal
$('div.main')
.children('p')
.addClass('paragraph')
.first()
.css('font-weight', 'bold')
.siblings()
.css('color', 'gray');
Using .end()
// Without .end() - need to reselect
$('#container').find('p').css('color', 'red');
$('#container').css('border', '1px solid blue');
// With .end() - return to previous selection
$('#container')
.find('p')
.css('color', 'red')
.end() // Back to #container
.css('border', '1px solid blue');
// Multiple .end() calls
$('ul')
.find('li')
.eq(2)
.css('color', 'red')
.end() // Back to all li
.first()
.css('font-weight', 'bold')
.end() // Back to all li
.end() // Back to ul
.css('border', '1px solid black');
// Example: Nested modifications
$('.menu')
.find('.menu-item')
.addClass('item')
.find('.submenu')
.addClass('sub')
.end() // Back to .menu-item
.end() // Back to .menu
.css('display', 'block');
Chaining with Events
// Chain event handlers
$('#button')
.click(function() {
console.log('Clicked');
})
.hover(
function() { $(this).addClass('hover'); },
function() { $(this).removeClass('hover'); }
)
.dblclick(function() {
console.log('Double clicked');
});
// Multiple events on same handler
$('#input')
.on('focus', function() {
$(this).addClass('focused');
})
.on('blur', function() {
$(this).removeClass('focused');
})
.on('keyup', function() {
console.log($(this).val());
});
// Setup and initial state
$('.toggle-button')
.text('Show')
.attr('data-state', 'hidden')
.on('click', function() {
$('.content').slideToggle();
})
.css('cursor', 'pointer');
Chaining Animations
// Animation sequence
$('#box')
.fadeOut(500)
.fadeIn(500)
.slideUp(500)
.slideDown(500);
// Animation with modifications
$('#element')
.hide()
.html('New content')
.fadeIn(1000)
.animate({ marginLeft: '100px' }, 500)
.addClass('positioned');
// Complex animation chain
$('.item')
.css('opacity', 0)
.slideDown(500)
.animate({ opacity: 1 }, 500)
.delay(1000)
.animate({ marginLeft: '50px' }, 500)
.queue(function() {
$(this).addClass('completed').dequeue();
});
// Callback alternative to chaining
$('#box')
.fadeOut(500, function() {
$(this).html('Changed').fadeIn(500);
});
Chaining with .add()
// Add more elements to selection
$('div')
.add('p')
.add('.special')
.css('border', '1px solid red');
// Add with context
$('.item')
.add('.other-item', '#container')
.addClass('highlighted');
// Add created elements
$('li')
.add('<li>New Item</li>')
.appendTo('ul');
// Example: Select multiple related elements
$('#header')
.add('#footer')
.add('.sidebar')
.css('background-color', '#f0f0f0');
Best Practices
// Good: Cache and chain
var $box = $('#box');
$box
.addClass('active')
.css('color', 'red')
.fadeIn();
// Good: Break long chains for readability
$('#element')
.css('color', 'red')
.addClass('highlight')
.fadeIn(500)
.animate({ marginLeft: '100px' }, 500);
// Bad: Too complex, hard to debug
$('div').find('p').filter(':first').css('color','red').parent().siblings().find('span').addClass('active');
// Good: Break into logical steps
$('div')
.find('p')
.filter(':first')
.css('color', 'red')
.parent()
.siblings()
.find('span')
.addClass('active');
// When not to chain
// If you need the result of intermediate steps
var $items = $('li');
var count = $items.length; // Need this value
$items.addClass('item');
// Use .end() to return to previous selection
$('#container')
.find('.item')
.css('color', 'blue')
.end() // Better than reselecting #container
.css('border', '1px solid black');
Complete Example
// Initialize and setup component
$('#notification')
.hide()
.html('<p>Action completed successfully!</p>')
.addClass('success')
.css({
position: 'fixed',
top: '20px',
right: '20px'
})
.fadeIn(500)
.delay(3000)
.fadeOut(500, function() {
$(this).remove();
});
// Form validation and submission
$('#submitBtn')
.prop('disabled', false)
.removeClass('disabled')
.addClass('active')
.on('click', function() {
$(this)
.prop('disabled', true)
.text('Submitting...')
.parent('form')
.find('input')
.prop('readonly', true)
.end()
.submit();
});
// Menu system
$('.menu-toggle')
.addClass('clickable')
.attr('role', 'button')
.attr('aria-expanded', 'false')
.on('click', function() {
$(this)
.toggleClass('active')
.attr('aria-expanded', function(i, attr) {
return attr === 'true' ? 'false' : 'true';
})
.next('.menu')
.slideToggle(300);
});
Practice Tasks
- Chain CSS changes, class additions, and animations.
- Use .end() to navigate back in a selection chain.
- Create a notification system using chained methods.
- Build a dropdown menu with chained events and effects.
Key Takeaways
- Chaining makes code cleaner and more efficient.
- Most jQuery methods return the jQuery object for chaining.
- Use .end() to return to the previous selection.
- Break long chains into multiple lines for readability.
- Not all methods are chainable (e.g., .text() getter, .val() getter).
What's Next?
Next Topic: Learn Callbacks.
Callbacks: Function After Function
Execute code after operations complete
What are Callbacks?
A callback is a function passed as an argument to another function, to be executed after the first function completes.
Basic Callbacks
// Animation with callback
$('#box').fadeOut(1000, function() {
console.log('Fade out complete');
alert('Animation finished!');
});
// Hide with callback
$('#element').hide('slow', function() {
$(this).remove(); // Remove after hiding
});
// Slide with callback
$('#menu').slideUp(500, function() {
$('#content').slideDown(500);
});
// Multiple animations with callbacks
$('#box').fadeOut(500, function() {
$('#box2').fadeOut(500, function() {
$('#box3').fadeOut(500, function() {
alert('All done!');
});
});
});
Callbacks with 'this'
// 'this' refers to the DOM element
$('#box').fadeOut(1000, function() {
console.log(this); // DOM element
$(this).remove(); // Wrap in jQuery
});
// Multiple elements with callbacks
$('div').fadeOut(500, function() {
// Runs once for EACH div
$(this).remove();
});
// Counter example
var count = 0;
$('li').fadeOut(500, function() {
count++;
if (count === $('li').length) {
console.log('All items hidden');
}
});
// Modify after animation
$('.item').slideUp(500, function() {
$(this)
.html('New content')
.removeClass('old')
.addClass('new')
.slideDown(500);
});
Ajax Callbacks
// Success callback
$.ajax({
url: '/api/data',
success: function(response) {
console.log('Success:', response);
$('#result').html(response);
}
});
// Multiple callbacks
$.ajax({
url: '/api/users',
beforeSend: function() {
$('#loading').show();
},
success: function(data) {
console.log('Data received:', data);
displayUsers(data);
},
error: function(xhr, status, error) {
console.log('Error:', error);
alert('Failed to load users');
},
complete: function() {
$('#loading').hide();
console.log('Request complete');
}
});
// Shorthand with callbacks
$.get('/api/data', function(response) {
$('#content').html(response);
});
$.post('/api/submit', { name: 'John' }, function(result) {
alert('Submitted: ' + result);
});
Event Callbacks
// Click callback
$('#button').click(function() {
console.log('Button clicked');
$(this).text('Clicked!');
});
// Named callback function
function handleClick() {
alert('Button was clicked');
}
$('#button').click(handleClick);
// Callback with parameters
$('#button').on('click', { msg: 'Hello' }, function(event) {
alert(event.data.msg);
});
// Multiple event callbacks
$('#input')
.focus(function() {
$(this).addClass('focused');
})
.blur(function() {
$(this).removeClass('focused');
})
.change(function() {
console.log('Value changed to:', $(this).val());
});
Callback Hell and Solutions
// BAD: Callback hell (pyramid of doom)
$('#step1').fadeOut(500, function() {
$('#step2').fadeIn(500, function() {
$('#step3').fadeOut(500, function() {
$('#step4').fadeIn(500, function() {
alert('All done!');
});
});
});
});
// BETTER: Named functions
function hideStep1() {
$('#step1').fadeOut(500, showStep2);
}
function showStep2() {
$('#step2').fadeIn(500, hideStep3);
}
function hideStep3() {
$('#step3').fadeOut(500, showStep4);
}
function showStep4() {
$('#step4').fadeIn(500, allDone);
}
function allDone() {
alert('All done!');
}
hideStep1();
// BEST: Promises (see Deferred/Promises section)
$('#step1').fadeOut(500).promise()
.then(function() { return $('#step2').fadeIn(500).promise(); })
.then(function() { return $('#step3').fadeOut(500).promise(); })
.then(function() { return $('#step4').fadeIn(500).promise(); })
.then(function() { alert('All done!'); });
$.Callbacks() Object
// Create callbacks object
var callbacks = $.Callbacks();
// Add callbacks
callbacks.add(function(msg) {
console.log('Callback 1: ' + msg);
});
callbacks.add(function(msg) {
console.log('Callback 2: ' + msg);
});
// Fire all callbacks
callbacks.fire('Hello');
// Logs: 'Callback 1: Hello', 'Callback 2: Hello'
// With options
var callbacks = $.Callbacks('once memory');
// 'once' - fire only once
// 'memory' - remember last fired value
callbacks.add(function(value) {
console.log(value);
});
callbacks.fire('First'); // Logs: 'First'
callbacks.fire('Second'); // Nothing (once)
// Add callback after fire with memory
callbacks.add(function(value) {
console.log('Late: ' + value);
});
// Logs: 'Late: First' (remembers last value)
// Remove callbacks
function callback1() { console.log('CB1'); }
function callback2() { console.log('CB2'); }
callbacks.add(callback1);
callbacks.add(callback2);
callbacks.remove(callback1);
callbacks.fire(); // Only logs 'CB2'
Practical Callback Examples
// Loading indicator
function loadData(url, callback) {
$('#loading').show();
$.get(url, function(data) {
$('#loading').hide();
if (callback) {
callback(data);
}
});
}
loadData('/api/users', function(users) {
displayUsers(users);
});
// Sequential animations
function animateSequence(elements, callback) {
var $current = $(elements.shift());
if ($current.length) {
$current.fadeOut(500, function() {
if (elements.length > 0) {
animateSequence(elements, callback);
} else if (callback) {
callback();
}
});
}
}
animateSequence(['#box1', '#box2', '#box3'], function() {
alert('All boxes hidden');
});
// Form submission with validation
function submitForm(formSelector, callback) {
$(formSelector).submit(function(e) {
e.preventDefault();
var isValid = validateForm(this);
if (isValid) {
$.post($(this).attr('action'), $(this).serialize(),
function(response) {
if (callback) {
callback(response);
}
}
);
}
});
}
submitForm('#contactForm', function(response) {
alert('Form submitted: ' + response.message);
});
// Image loader with callback
function loadImage(src, callback) {
var img = new Image();
img.onload = function() {
callback(null, img);
};
img.onerror = function() {
callback('Failed to load image');
};
img.src = src;
}
loadImage('photo.jpg', function(error, img) {
if (error) {
console.log(error);
} else {
$('body').append(img);
}
});
Callback Best Practices
// Always check if callback exists
function doSomething(callback) {
// Do work...
if (typeof callback === 'function') {
callback();
}
}
// Error-first callbacks (Node.js style)
function fetchData(url, callback) {
$.get(url)
.done(function(data) {
callback(null, data); // No error, pass data
})
.fail(function(error) {
callback(error); // Pass error
});
}
fetchData('/api/data', function(error, data) {
if (error) {
console.log('Error:', error);
} else {
console.log('Data:', data);
}
});
// Context binding
var obj = {
name: 'MyObject',
doSomething: function(callback) {
// ...
callback.call(this); // Call with obj as 'this'
}
};
// Avoid anonymous functions when debugging
// BAD
$('#btn').click(function() { /* ... */ });
// GOOD - named for debugging
function handleButtonClick() {
/* ... */
}
$('#btn').click(handleButtonClick);
Practice Tasks
- Create an animation that runs a callback when complete.
- Build an Ajax function that accepts success/error callbacks.
- Implement sequential animations using callbacks.
- Create a reusable function with optional callback parameter.
Key Takeaways
- Callbacks execute after operations complete.
- 'this' in callbacks refers to the DOM element.
- Always check if callback parameter is a function.
- Avoid deeply nested callbacks (callback hell).
- Use named functions for better debugging.
- Consider Promises for complex async operations.
What's Next?
Next Topic: Learn Filtering and Searching.
Deferred and Promises
Handle asynchronous operations elegantly
What are Promises?
Promises provide a better way to handle asynchronous operations than callbacks, avoiding callback hell and providing better error handling.
Basic Promise Usage
// Ajax returns a promise
$.ajax('/api/users')
.done(function(data) {
console.log('Success:', data);
})
.fail(function(error) {
console.log('Error:', error);
})
.always(function() {
console.log('Complete');
});
// Modern then/catch syntax
$.ajax('/api/users')
.then(
function(data) { // Success
console.log('Success:', data);
},
function(error) { // Error
console.log('Error:', error);
}
);
// Chained then
$.get('/api/user/1')
.then(function(user) {
console.log('User:', user.name);
return $.get('/api/user/' + user.id + '/posts');
})
.then(function(posts) {
console.log('Posts:', posts);
})
.fail(function(error) {
console.log('Error:', error);
});
Creating Deferred Objects
// Create deferred object
function loadData() {
var deferred = $.Deferred();
setTimeout(function() {
var data = { name: 'John', age: 30 };
deferred.resolve(data); // Success
}, 1000);
return deferred.promise();
}
loadData()
.done(function(data) {
console.log('Loaded:', data);
});
// With rejection
function fetchUser(id) {
var deferred = $.Deferred();
$.get('/api/user/' + id)
.done(function(data) {
deferred.resolve(data);
})
.fail(function(error) {
deferred.reject(error);
});
return deferred.promise();
}
fetchUser(123)
.done(function(user) {
console.log('User:', user);
})
.fail(function(error) {
console.log('Error:', error);
});
Promise States
// Deferred states
var deferred = $.Deferred();
console.log(deferred.state()); // 'pending'
deferred.resolve('Success');
console.log(deferred.state()); // 'resolved'
// Check state
var promise = $.get('/api/data');
if (promise.state() === 'pending') {
console.log('Loading...');
}
// Resolved deferred
var resolved = $.Deferred().resolve('Data').promise();
resolved.done(function(data) {
console.log(data); // 'Data' - runs immediately
});
// Rejected deferred
var rejected = $.Deferred().reject('Error').promise();
rejected.fail(function(error) {
console.log(error); // 'Error' - runs immediately
});
Multiple Promises with $.when()
// Wait for multiple Ajax calls
var users = $.get('/api/users');
var posts = $.get('/api/posts');
var comments = $.get('/api/comments');
$.when(users, posts, comments)
.done(function(usersData, postsData, commentsData) {
console.log('Users:', usersData[0]);
console.log('Posts:', postsData[0]);
console.log('Comments:', commentsData[0]);
})
.fail(function() {
console.log('One or more requests failed');
});
// With non-promise values
$.when($.get('/api/data'), 'immediate value', 123)
.done(function(ajaxData, str, num) {
console.log(ajaxData[0], str, num);
});
// Dynamic number of promises
var promises = [];
for (var i = 1; i <= 5; i++) {
promises.push($.get('/api/user/' + i));
}
$.when.apply($, promises)
.done(function() {
console.log('All users loaded');
console.log(arguments);
});
Promise Chaining
// Chain promises
$.get('/api/user/1')
.then(function(user) {
console.log('User:', user.name);
return $.get('/api/user/' + user.id + '/profile');
})
.then(function(profile) {
console.log('Profile:', profile);
return $.get('/api/user/' + profile.userId + '/settings');
})
.then(function(settings) {
console.log('Settings:', settings);
})
.fail(function(error) {
console.log('Error at any step:', error);
});
// Transform data in chain
$.get('/api/users')
.then(function(users) {
// Filter users
return users.filter(function(u) {
return u.age > 18;
});
})
.then(function(adults) {
// Map to names
return adults.map(function(u) {
return u.name;
});
})
.done(function(names) {
console.log('Adult names:', names);
});
Animation Promises
// Get animation promise
$('#box').fadeOut(1000).promise()
.done(function() {
console.log('Fade out complete');
});
// Chain animations with promises
$('#box1').fadeOut(500).promise()
.then(function() {
return $('#box2').fadeOut(500).promise();
})
.then(function() {
return $('#box3').fadeOut(500).promise();
})
.done(function() {
alert('All animations complete');
});
// Wait for multiple animations
var anim1 = $('#box1').fadeOut(1000).promise();
var anim2 = $('#box2').slideUp(1000).promise();
var anim3 = $('#box3').fadeOut(1000).promise();
$.when(anim1, anim2, anim3)
.done(function() {
console.log('All animations finished');
});
// Animation queue promise
$('.items').fadeOut(500).promise('fx')
.done(function() {
console.log('All items faded');
});
Error Handling
// Fail handler
$.get('/api/data')
.done(function(data) {
console.log('Success');
})
.fail(function(xhr, status, error) {
console.log('Failed:', error);
// Show error to user
$('#error').text('Failed to load data');
})
.always(function() {
$('#loading').hide();
});
// Catch in chain
$.get('/api/user/1')
.then(function(user) {
if (!user.active) {
throw new Error('User not active');
}
return user;
})
.then(function(user) {
return $.get('/api/user/' + user.id + '/posts');
})
.fail(function(error) {
console.log('Error:', error);
});
// Recover from error
$.get('/api/data')
.then(
function(data) {
return data;
},
function(error) {
// Recover by returning default
return { default: true };
}
)
.done(function(data) {
console.log('Data:', data);
});
Progress Notifications
// Notify progress
function uploadFile(file) {
var deferred = $.Deferred();
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
var percent = (e.loaded / e.total) * 100;
deferred.notify(percent);
}
};
xhr.onload = function() {
deferred.resolve(xhr.responseText);
};
xhr.onerror = function() {
deferred.reject('Upload failed');
};
xhr.open('POST', '/api/upload');
xhr.send(file);
return deferred.promise();
}
uploadFile(myFile)
.progress(function(percent) {
$('#progress').text(Math.round(percent) + '%');
})
.done(function(response) {
console.log('Upload complete');
})
.fail(function(error) {
console.log('Upload failed');
});
// Multiple progress updates
function processItems(items) {
var deferred = $.Deferred();
var processed = 0;
items.forEach(function(item, index) {
setTimeout(function() {
// Process item...
processed++;
deferred.notify(processed / items.length * 100);
if (processed === items.length) {
deferred.resolve('All done');
}
}, index * 1000);
});
return deferred.promise();
}
processItems([1, 2, 3, 4, 5])
.progress(function(percent) {
console.log(Math.round(percent) + '% complete');
});
Complete Example
// User profile loader
function loadUserProfile(userId) {
var user, posts, friends;
return $.get('/api/user/' + userId)
.then(function(userData) {
user = userData;
return $.when(
$.get('/api/user/' + userId + '/posts'),
$.get('/api/user/' + userId + '/friends')
);
})
.then(function(postsData, friendsData) {
posts = postsData[0];
friends = friendsData[0];
return { user: user, posts: posts, friends: friends };
});
}
// Usage
loadUserProfile(123)
.done(function(profile) {
$('#userName').text(profile.user.name);
profile.posts.forEach(function(post) {
$('#posts').append('<div>' + post.title + '</div>');
});
profile.friends.forEach(function(friend) {
$('#friends').append('<div>' + friend.name + '</div>');
});
})
.fail(function(error) {
$('#error').text('Failed to load profile');
})
.always(function() {
$('#loading').hide();
});
// Retry with promise
function retryAjax(url, retries) {
var deferred = $.Deferred();
function attempt(n) {
$.get(url)
.done(function(data) {
deferred.resolve(data);
})
.fail(function(error) {
if (n === 0) {
deferred.reject(error);
} else {
setTimeout(function() {
attempt(n - 1);
}, 1000);
}
});
}
attempt(retries);
return deferred.promise();
}
retryAjax('/api/data', 3)
.done(function(data) {
console.log('Success:', data);
})
.fail(function() {
console.log('Failed after retries');
});
Practice Tasks
- Load multiple API endpoints with $.when().
- Create a deferred object for custom async operation.
- Chain promises for sequential data loading.
- Implement progress tracking for file upload.
Key Takeaways
- Promises avoid callback hell with cleaner syntax.
- Use $.Deferred() to create custom promises.
- $.when() waits for multiple promises.
- Chain promises with .then() for sequential operations.
- .fail() handles errors anywhere in the chain.
What's Next?
Next Topic: Learn Animation Queue.
Custom Events
Create and trigger your own events
Why Custom Events?
Custom events allow components to communicate without tight coupling. Great for building modular, reusable code.
Triggering Custom Events
// Define custom event handler
$('#element').on('customEvent', function() {
console.log('Custom event triggered');
});
// Trigger custom event
$('#element').trigger('customEvent');
// Trigger with data
$('#element').on('customEvent', function(event, data) {
console.log('Data:', data);
});
$('#element').trigger('customEvent', { name: 'John', age: 30 });
// Multiple data arguments
$('#element').on('customEvent', function(event, arg1, arg2, arg3) {
console.log(arg1, arg2, arg3);
});
$('#element').trigger('customEvent', ['Hello', 'World', 123]);
// triggerHandler - doesn't bubble, doesn't trigger default
$('#element').triggerHandler('customEvent');
Event Namespacing
// Add namespaced events
$('#element').on('click.myPlugin', function() {
console.log('Plugin click');
});
$('#element').on('click.otherPlugin', function() {
console.log('Other plugin click');
});
// Trigger specific namespace
$('#element').trigger('click.myPlugin');
// Remove specific namespace
$('#element').off('click.myPlugin');
// click.otherPlugin still exists
// Multiple namespaces
$('#element').on('click.plugin.special', function() {
console.log('Multi-namespace');
});
// Trigger by any namespace part
$('#element').trigger('click.plugin');
$('#element').trigger('click.special');
// Remove all in namespace
$('#element').off('.myPlugin'); // Removes all myPlugin events
Custom Event Objects
// Create custom event object
var event = $.Event('customEvent');
event.customData = 'some data';
$('#element').trigger(event);
// Access in handler
$('#element').on('customEvent', function(event) {
console.log(event.customData);
});
// Prevent default and stop propagation
var event = $.Event('customEvent');
event.preventDefault();
event.stopPropagation();
$('#element').trigger(event);
// Custom properties
var event = $.Event('userLogin', {
userId: 123,
userName: 'John',
timestamp: Date.now()
});
$('#app').trigger(event);
$('#app').on('userLogin', function(event) {
console.log('User', event.userName, 'logged in');
});
Practical Custom Events
// Example 1: Tab system
$('.tab').click(function() {
var tabId = $(this).data('tab');
// Trigger custom event
$(this).trigger('tabChange', [tabId]);
});
$(document).on('tabChange', function(event, tabId) {
$('.tab-content').hide();
$('#' + tabId).show();
console.log('Switched to tab:', tabId);
});
// Example 2: Form validation
$('#form').submit(function(e) {
e.preventDefault();
// Trigger validation event
$(this).trigger('formValidate');
});
$('#form').on('formValidate', function() {
var isValid = true;
$(this).find('input').each(function() {
if ($(this).val() === '') {
isValid = false;
$(this).trigger('fieldError', ['Field required']);
}
});
if (isValid) {
$(this).trigger('formValid');
}
});
$('input').on('fieldError', function(event, message) {
$(this).addClass('error');
$(this).next('.error-message').text(message);
});
$('#form').on('formValid', function() {
$(this).find('button').prop('disabled', true).text('Submitting...');
// Submit via Ajax
});
// Example 3: Shopping cart
function addToCart(productId) {
// Add logic...
$(document).trigger('cartUpdate', [{
productId: productId,
action: 'add'
}]);
}
$(document).on('cartUpdate', function(event, data) {
updateCartCount();
showNotification('Item added to cart');
// Analytics
trackCartEvent(data);
});
Event Communication Pattern
// Component A - User Profile
var UserProfile = {
init: function() {
$('#loginBtn').click(this.login);
},
login: function() {
// Login logic...
var userData = { id: 123, name: 'John' };
// Broadcast event
$(document).trigger('user:login', [userData]);
}
};
// Component B - Shopping Cart
var ShoppingCart = {
init: function() {
$(document).on('user:login', this.onUserLogin.bind(this));
},
onUserLogin: function(event, userData) {
console.log('Cart: User logged in', userData.name);
this.loadUserCart(userData.id);
},
loadUserCart: function(userId) {
// Load cart for user
}
};
// Component C - Notifications
var Notifications = {
init: function() {
$(document).on('user:login', this.showWelcome.bind(this));
},
showWelcome: function(event, userData) {
this.show('Welcome back, ' + userData.name + '!');
},
show: function(message) {
$('<div>').addClass('notification').text(message)
.appendTo('body').fadeIn().delay(3000).fadeOut();
}
};
// Initialize all
UserProfile.init();
ShoppingCart.init();
Notifications.init();
Custom Event Best Practices
// Use descriptive event names
// Good
$(document).trigger('cart:itemAdded', [item]);
$(document).trigger('user:loggedOut');
$(document).trigger('modal:opened', [modalId]);
// Bad
$(document).trigger('update');
$(document).trigger('change');
// Namespace by feature
$(document).on('cart:itemAdded.analytics', trackAddToCart);
$(document).on('cart:itemAdded.ui', updateCartUI);
$(document).on('cart:itemAdded.storage', saveToLocalStorage);
// Easy to remove all cart.analytics events
$(document).off('.analytics');
// Use consistent naming conventions
// Pattern: noun:verb
'user:login'
'user:logout'
'cart:update'
'modal:open'
'modal:close'
'form:submit'
'form:validate'
// Prevent infinite loops
var updating = false;
$(document).on('data:change', function() {
if (updating) return;
updating = true;
// Update logic
updating = false;
});
// Or use one() for single execution
$(document).one('init:complete', function() {
console.log('Initialized');
});
Event Aggregator Pattern
// Event aggregator
var EventBus = {
$el: $({}),
on: function(event, handler) {
this.$el.on(event, handler);
},
off: function(event, handler) {
this.$el.off(event, handler);
},
trigger: function(event, data) {
this.$el.trigger(event, data);
}
};
// Usage
EventBus.on('user:login', function(event, user) {
console.log('User logged in:', user);
});
EventBus.trigger('user:login', { id: 123, name: 'John' });
// With promises
var EventBus = {
$el: $({}),
trigger: function(event, data) {
var deferred = $.Deferred();
this.$el.trigger(event, [data, deferred]);
return deferred.promise();
}
};
EventBus.on('user:login', function(event, user, deferred) {
$.post('/api/login', user)
.done(function(response) {
deferred.resolve(response);
})
.fail(function(error) {
deferred.reject(error);
});
});
EventBus.trigger('user:login', { username: 'john' })
.done(function(response) {
console.log('Login success');
})
.fail(function(error) {
console.log('Login failed');
});
Complete Example
// Application with custom events
var App = {
init: function() {
this.bindEvents();
Header.init();
Cart.init();
Products.init();
},
bindEvents: function() {
// Global app events
$(document).on('app:error', this.handleError);
$(document).on('app:loading', this.showLoading);
$(document).on('app:loaded', this.hideLoading);
},
handleError: function(event, error) {
alert('Error: ' + error.message);
},
showLoading: function() {
$('#loader').show();
},
hideLoading: function() {
$('#loader').hide();
}
};
var Products = {
init: function() {
$('.add-to-cart').on('click', this.addToCart.bind(this));
},
addToCart: function(e) {
var $btn = $(e.currentTarget);
var productId = $btn.data('product-id');
var productName = $btn.data('product-name');
var price = parseFloat($btn.data('price'));
$(document).trigger('cart:add', [{
id: productId,
name: productName,
price: price
}]);
}
};
var Cart = {
items: [],
init: function() {
$(document).on('cart:add', this.onAdd.bind(this));
$(document).on('cart:remove', this.onRemove.bind(this));
},
onAdd: function(event, product) {
this.items.push(product);
this.update();
$(document).trigger('cart:updated', [this.items]);
},
onRemove: function(event, productId) {
this.items = this.items.filter(function(item) {
return item.id !== productId;
});
this.update();
$(document).trigger('cart:updated', [this.items]);
},
update: function() {
var total = this.items.reduce(function(sum, item) {
return sum + item.price;
}, 0);
$('#cart-count').text(this.items.length);
$('#cart-total').text('$' + total.toFixed(2));
}
};
var Header = {
init: function() {
$(document).on('cart:updated', this.updateCartBadge);
$(document).on('user:login', this.showUserMenu);
$(document).on('user:logout', this.hideUserMenu);
},
updateCartBadge: function(event, items) {
$('#cart-badge').text(items.length).toggle(items.length > 0);
},
showUserMenu: function(event, user) {
$('#user-menu').show();
$('#login-btn').hide();
$('#user-name').text(user.name);
},
hideUserMenu: function() {
$('#user-menu').hide();
$('#login-btn').show();
}
};
// Start app
$(document).ready(function() {
App.init();
});
Practice Tasks
- Create a custom event for form validation.
- Build an event-based notification system.
- Implement component communication with custom events.
- Create an event aggregator for your application.
Key Takeaways
- trigger() fires custom events with optional data.
- Use namespaces to organize and remove events easily.
- Custom events enable loose coupling between components.
- Follow naming conventions: noun:verb pattern.
- Event aggregator centralizes event management.
What's Next?
Next Topic: Learn jQuery UI.
Data Storage: Store Data on Elements
Attach custom data to DOM elements safely
Why Use data()?
jQuery's .data() method provides a safe way to store arbitrary data on DOM elements without modifying the DOM or causing memory leaks.
Basic Data Storage
// Store data
$('#user').data('userId', 123);
$('#user').data('role', 'admin');
$('#user').data('active', true);
// Retrieve data
var userId = $('#user').data('userId'); // 123
var role = $('#user').data('role'); // 'admin'
var active = $('#user').data('active'); // true
// Store object
$('#user').data('profile', {
name: 'John Doe',
email: 'john@example.com',
age: 30
});
var profile = $('#user').data('profile');
console.log(profile.name); // 'John Doe'
// Store multiple at once
$('#user').data({
userId: 456,
role: 'user',
verified: true
});
HTML5 data-* Attributes
// HTML: <div id="product" data-id="123" data-price="29.99" data-in-stock="true">
// jQuery automatically reads data-* attributes
var productId = $('#product').data('id'); // 123 (number)
var price = $('#product').data('price'); // 29.99 (number)
var inStock = $('#product').data('in-stock'); // true (boolean)
// Note: jQuery converts types automatically
// Camel case conversion
// HTML: data-user-name → JavaScript: userName
var userName = $('#element').data('user-name');
var userName = $('#element').data('userName'); // Same result
// Get all data attributes
var allData = $('#product').data();
console.log(allData);
// { id: 123, price: 29.99, inStock: true }
// Set programmatically (doesn't update HTML)
$('#product').data('discount', 10);
// HTML attribute unchanged, but data() will return 10
Difference: attr() vs data()
// attr() modifies HTML attribute
$('#element').attr('data-value', '100');
console.log($('#element').attr('data-value')); // '100' (string)
// data() stores in jQuery's internal cache
$('#element').data('value', 100);
console.log($('#element').data('value')); // 100 (number)
// data() initial value from HTML attribute
// HTML: <div id="test" data-count="5">
$('#test').data('count'); // 5 (first call, reads from HTML)
// Modify with data()
$('#test').data('count', 10);
$('#test').data('count'); // 10
$('#test').attr('data-count'); // Still '5' (HTML unchanged)
// Best practice:
// - Use data() for dynamic runtime data
// - Use attr() when you need to modify HTML attribute
Remove Data
// Store data
$('#element').data('key1', 'value1');
$('#element').data('key2', 'value2');
// Remove single key
$('#element').removeData('key1');
console.log($('#element').data('key1')); // undefined
// Remove multiple keys
$('#element').removeData('key1 key2');
// or
$('#element').removeData(['key1', 'key2']);
// Remove all data
$('#element').removeData();
// Check if data exists
if ($('#element').data('userId') !== undefined) {
console.log('User ID exists');
}
Practical Use Cases
// Example 1: Store item state
$('.todo-item').each(function(index) {
$(this).data('index', index);
$(this).data('completed', false);
$(this).data('createdAt', new Date());
});
$('.todo-item').click(function() {
var index = $(this).data('index');
var completed = $(this).data('completed');
$(this).data('completed', !completed);
$(this).toggleClass('completed');
});
// Example 2: Store API response
$('.user-card').click(function() {
var $card = $(this);
var userId = $card.data('user-id');
// Check if data already loaded
if ($card.data('userDetails')) {
displayUser($card.data('userDetails'));
} else {
$.get('/api/users/' + userId, function(user) {
$card.data('userDetails', user); // Cache response
displayUser(user);
});
}
});
// Example 3: Form validation state
$('#email').blur(function() {
var email = $(this).val();
var isValid = validateEmail(email);
$(this).data('valid', isValid);
$(this).toggleClass('error', !isValid);
});
$('#form').submit(function(e) {
var emailValid = $('#email').data('valid');
if (!emailValid) {
e.preventDefault();
alert('Fix validation errors');
}
});
// Example 4: Store original values
$('input').focus(function() {
$(this).data('originalValue', $(this).val());
});
$('.cancel').click(function() {
$('input').each(function() {
var original = $(this).data('originalValue');
if (original !== undefined) {
$(this).val(original);
}
});
});
Store Complex Data
// Store array
$('#list').data('items', ['Apple', 'Banana', 'Orange']);
var items = $('#list').data('items');
items.push('Grape');
$('#list').data('items', items);
// Store object
$('#user').data('profile', {
name: 'John',
age: 30,
address: {
city: 'New York',
country: 'USA'
},
hobbies: ['reading', 'gaming']
});
var profile = $('#user').data('profile');
console.log(profile.address.city); // 'New York'
// Store function
$('#element').data('callback', function() {
console.log('Callback executed');
});
var callback = $('#element').data('callback');
callback(); // Execute stored function
// Store jQuery objects
$('#container').data('relatedElement', $('.sidebar'));
var $sidebar = $('#container').data('relatedElement');
$sidebar.addClass('active');
Data with Event Delegation
// Add items dynamically
function addItem(id, name) {
var $item = $('<div class="item">' + name + '</div>');
$item.data('itemId', id);
$item.data('itemName', name);
$('#container').append($item);
}
addItem(1, 'Item 1');
addItem(2, 'Item 2');
// Event delegation with data
$('#container').on('click', '.item', function() {
var id = $(this).data('itemId');
var name = $(this).data('itemName');
console.log('Clicked:', id, name);
});
// Delete item
$('#container').on('click', '.delete', function() {
var $item = $(this).closest('.item');
var id = $item.data('itemId');
// API call to delete
$.post('/api/items/' + id + '/delete', function() {
$item.remove();
});
});
Performance Considerations
// Good: Store data once, reuse
$('.product').each(function() {
var $this = $(this);
$.get('/api/product/' + $this.data('id'), function(product) {
$this.data('productDetails', product); // Cache
});
});
// Later
$('.product').click(function() {
var product = $(this).data('productDetails');
if (product) {
displayProduct(product); // No new API call
}
});
// Memory cleanup
$('#element').remove(); // jQuery auto-cleans data
// Manual cleanup for detached elements
var $element = $('#item').detach();
$element.removeData(); // Clean up
// Don't store large data
// BAD: Store entire API response
$('.item').data('response', largeAPIResponse);
// GOOD: Store only needed data
$('.item').data('id', largeAPIResponse.id);
$('.item').data('name', largeAPIResponse.name);
// Namespace data keys
$('#element').data('myPlugin.setting1', 'value');
$('#element').data('myPlugin.setting2', 'value');
Complete Example
// Initialize cart
$('#cart').data('items', []);
$('#cart').data('total', 0);
// Add to cart
$('.add-to-cart').click(function() {
var $product = $(this).closest('.product');
var item = {
id: $product.data('product-id'),
name: $product.data('product-name'),
price: parseFloat($product.data('price')),
quantity: 1
};
var items = $('#cart').data('items');
// Check if exists
var existing = items.find(function(i) {
return i.id === item.id;
});
if (existing) {
existing.quantity++;
} else {
items.push(item);
}
$('#cart').data('items', items);
updateCartTotal();
updateCartDisplay();
});
function updateCartTotal() {
var items = $('#cart').data('items');
var total = items.reduce(function(sum, item) {
return sum + (item.price * item.quantity);
}, 0);
$('#cart').data('total', total);
$('#cartTotal').text('$' + total.toFixed(2));
}
function updateCartDisplay() {
var items = $('#cart').data('items');
$('#cartCount').text(items.length);
$('#cartItems').html('');
items.forEach(function(item) {
$('#cartItems').append(
'<div>' + item.name + ' x' + item.quantity +
' = $' + (item.price * item.quantity).toFixed(2) + '</div>'
);
});
}
// Checkout
$('#checkout').click(function() {
var items = $('#cart').data('items');
var total = $('#cart').data('total');
$.post('/api/checkout', {
items: items,
total: total
}, function(response) {
alert('Order placed!');
$('#cart').data('items', []);
$('#cart').data('total', 0);
updateCartDisplay();
});
});
Practice Tasks
- Store user preferences on form elements.
- Cache API responses using .data().
- Build a to-do list storing state with data().
- Create a rating system storing ratings on elements.
Key Takeaways
- .data() stores data safely without modifying HTML.
- Automatically reads HTML5 data-* attributes.
- Converts types (strings to numbers/booleans).
- Use for caching, state management, and configuration.
- jQuery automatically cleans up data when elements removed.
What's Next?
Next Topic: Learn Custom Events.
Filtering and Searching
Find and filter elements dynamically
What is Filtering?
Filtering refines a set of selected elements based on criteria, while searching finds elements matching specific conditions.
filter() Method
// Filter by selector
$('li').filter('.active').css('font-weight', 'bold');
$('div').filter('#main').addClass('highlight');
// Filter by attribute
$('input').filter('[type="text"]').addClass('text-input');
$('a').filter('[href^="https"]').attr('target', '_blank');
// Filter by function
$('li').filter(function(index) {
return index % 2 === 0; // Even indexed items
}).css('background', '#f0f0f0');
$('div').filter(function() {
return $(this).children().length > 3;
}).addClass('has-many-children');
// Filter by multiple criteria
$('p').filter('.intro, .summary').css('font-style', 'italic');
// Chain filtering
$('li')
.filter('.item')
.filter(':visible')
.filter(function() {
return $(this).text().length > 10;
})
.addClass('long-item');
not() Method
// Exclude by selector
$('li').not('.exclude').fadeIn();
$('div').not('#special').css('color', 'gray');
// Exclude by element
var $first = $('li:first');
$('li').not($first).css('opacity', 0.5);
// Exclude by function
$('p').not(function() {
return $(this).text().length < 5;
}).addClass('long-paragraph');
// Exclude multiple
$('div').not('.header, .footer, .sidebar').addClass('content');
// Combine with other methods
$('input')
.not('[type="hidden"]')
.not(':disabled')
.addClass('editable');
has() Method
// Has specific descendant
$('div').has('p').addClass('has-paragraph');
$('li').has('ul').addClass('has-submenu');
// Has element matching selector
$('div').has('.important').css('border', '2px solid red');
// Has specific element
var $img = $('img.logo');
$('div').has($img).addClass('contains-logo');
// Practical example: Expand parents
$('.tree li').has('ul').each(function() {
$(this).prepend('<span class="expand">+</span>');
});
// Highlight containers with errors
$('.form-group').has('.error').addClass('field-error');
is() Method
// Check class
if ($('#element').is('.active')) {
console.log('Element is active');
}
// Check visibility
if ($('#box').is(':visible')) {
$('#box').fadeOut();
}
if ($('#box').is(':hidden')) {
$('#box').fadeIn();
}
// Check attributes
if ($('input').is('[readonly]')) {
console.log('Input is readonly');
}
// Check checked state
if ($('#checkbox').is(':checked')) {
console.log('Checkbox is checked');
}
// Check multiple conditions
if ($('p').is('.intro, .summary')) {
console.log('Paragraph is intro or summary');
}
// Use in event handler
$('li').click(function() {
if ($(this).is(':first-child')) {
alert('You clicked the first item');
}
});
find() Method
// Find all descendants
$('#container').find('p').addClass('paragraph');
$('div').find('.item').css('color', 'blue');
// Find by complex selector
$('#menu').find('li > a').attr('target', '_blank');
// Find and filter
$('#list')
.find('li')
.filter('.active')
.css('font-weight', 'bold');
// Nested finds
$('#container')
.find('.section')
.find('.item')
.addClass('nested-item');
// Find specific element
var $header = $('#container').find('h2:first');
// Example: Find and modify
$('.article').each(function() {
var $links = $(this).find('a[href^="http"]');
$links.attr('target', '_blank').addClass('external');
});
Live Search Implementation
// Simple list search
$('#searchInput').on('keyup', function() {
var query = $(this).val().toLowerCase();
$('li').filter(function() {
var text = $(this).text().toLowerCase();
return text.indexOf(query) === -1;
}).hide();
$('li').filter(function() {
var text = $(this).text().toLowerCase();
return text.indexOf(query) !== -1;
}).show();
});
// Better version with toggle
$('#searchInput').on('keyup', function() {
var query = $(this).val().toLowerCase();
$('li').each(function() {
var text = $(this).text().toLowerCase();
$(this).toggle(text.indexOf(query) !== -1);
});
});
// Search with highlight
$('#searchInput').on('keyup', function() {
var query = $(this).val().toLowerCase();
$('.item').each(function() {
var $item = $(this);
var text = $item.text().toLowerCase();
var matches = text.indexOf(query) !== -1 && query !== '';
$item.toggle(query === '' || matches);
$item.toggleClass('highlighted', matches);
});
});
Advanced Filtering
// Filter by multiple fields
function filterProducts() {
var category = $('#categoryFilter').val();
var minPrice = parseFloat($('#minPrice').val()) || 0;
var maxPrice = parseFloat($('#maxPrice').val()) || Infinity;
$('.product').each(function() {
var $product = $(this);
var productCategory = $product.data('category');
var productPrice = parseFloat($product.data('price'));
var categoryMatch = !category || productCategory === category;
var priceMatch = productPrice >= minPrice && productPrice <= maxPrice;
$product.toggle(categoryMatch && priceMatch);
});
}
$('#categoryFilter, #minPrice, #maxPrice').on('change', filterProducts);
// Tag-based filtering
$('.tag-filter').click(function() {
var tag = $(this).data('tag');
if (tag === 'all') {
$('.item').show();
} else {
$('.item').hide();
$('.item[data-tags*="' + tag + '"]').show();
}
$('.tag-filter').removeClass('active');
$(this).addClass('active');
});
// Checkbox filtering
function filterByCheckboxes() {
var selectedTags = [];
$('.filter-checkbox:checked').each(function() {
selectedTags.push($(this).val());
});
if (selectedTags.length === 0) {
$('.item').show();
return;
}
$('.item').each(function() {
var $item = $(this);
var itemTags = $item.data('tags').split(',');
var matches = selectedTags.some(function(tag) {
return itemTags.includes(tag);
});
$item.toggle(matches);
});
}
$('.filter-checkbox').change(filterByCheckboxes);
Search with Debounce
// Debounce function
function debounce(func, wait) {
var timeout;
return function() {
var context = this;
var args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
};
}
// Search with debounce
var searchItems = debounce(function() {
var query = $('#search').val().toLowerCase();
$('.item').each(function() {
var text = $(this).text().toLowerCase();
$(this).toggle(text.indexOf(query) !== -1);
});
}, 300); // Wait 300ms after typing stops
$('#search').on('keyup', searchItems);
// Show result count
var searchWithCount = debounce(function() {
var query = $('#search').val().toLowerCase();
var count = 0;
$('.item').each(function() {
var text = $(this).text().toLowerCase();
var matches = text.indexOf(query) !== -1;
$(this).toggle(matches);
if (matches) count++;
});
$('#resultCount').text(count + ' results');
}, 300);
$('#search').on('keyup', searchWithCount);
Complete Filter System
// Initialize
var $items = $('.product-item');
var filters = {
search: '',
category: 'all',
priceMin: 0,
priceMax: Infinity,
inStock: false
};
// Apply all filters
function applyFilters() {
$items.each(function() {
var $item = $(this);
var show = true;
// Search filter
if (filters.search) {
var text = $item.text().toLowerCase();
show = show && text.indexOf(filters.search) !== -1;
}
// Category filter
if (filters.category !== 'all') {
show = show && $item.data('category') === filters.category;
}
// Price filter
var price = parseFloat($item.data('price'));
show = show && price >= filters.priceMin && price <= filters.priceMax;
// Stock filter
if (filters.inStock) {
show = show && $item.data('in-stock') === true;
}
$item.toggle(show);
});
updateResultCount();
}
// Search input
$('#searchBox').on('keyup', debounce(function() {
filters.search = $(this).val().toLowerCase();
applyFilters();
}, 300));
// Category select
$('#categorySelect').on('change', function() {
filters.category = $(this).val();
applyFilters();
});
// Price range
$('#priceMin, #priceMax').on('change', function() {
filters.priceMin = parseFloat($('#priceMin').val()) || 0;
filters.priceMax = parseFloat($('#priceMax').val()) || Infinity;
applyFilters();
});
// Stock checkbox
$('#inStockOnly').on('change', function() {
filters.inStock = $(this).is(':checked');
applyFilters();
});
// Clear filters
$('#clearFilters').click(function() {
filters = {
search: '',
category: 'all',
priceMin: 0,
priceMax: Infinity,
inStock: false
};
$('#searchBox').val('');
$('#categorySelect').val('all');
$('#priceMin, #priceMax').val('');
$('#inStockOnly').prop('checked', false);
applyFilters();
});
function updateResultCount() {
var count = $items.filter(':visible').length;
$('#resultCount').text(count + ' of ' + $items.length + ' products');
}
// Initial display
applyFilters();
Practice Tasks
- Create a live search for a list of items.
- Build a multi-criteria filter system.
- Implement tag-based filtering with buttons.
- Add a "no results" message when nothing matches.
Key Takeaways
- .filter() refines selection by criteria.
- .not() excludes elements from selection.
- .has() finds elements containing specific descendants.
- .is() checks if element matches condition.
- Use debounce for performance on keyup searches.
What's Next?
Next Topic: Learn Debugging Tools.
Animation Queue
Control the order and flow of animations
Understanding the Queue
jQuery queues animations on elements, executing them in sequence. Understanding the queue helps control complex animation timing.
Default Animation Queuing
// Animations run in sequence (queued)
$('#box')
.fadeOut(1000) // First
.fadeIn(1000) // Second (waits for fadeOut)
.slideUp(1000) // Third (waits for fadeIn)
.slideDown(1000); // Fourth (waits for slideUp)
// CSS changes happen immediately (not queued)
$('#box')
.fadeOut(1000)
.css('background', 'red') // Happens immediately
.fadeIn(1000);
// Multiple clicks can queue up
$('#button').click(function() {
$('#box').fadeOut().fadeIn(); // Each click adds to queue
});
// Problem: Animation queue buildup
$('#box').hover(
function() { $(this).fadeOut(); },
function() { $(this).fadeIn(); }
);
// Rapid hovers = many queued animations
stop() Method
// Stop current animation
$('#box').stop();
// Stop and clear queue
$('#box').stop(true);
// Stop, clear queue, and jump to end
$('#box').stop(true, true);
// Better hover (prevent queue buildup)
$('#box').hover(
function() {
$(this).stop(true, true).fadeOut();
},
function() {
$(this).stop(true, true).fadeIn();
}
);
// Smooth hover effect
$('.menu-item').hover(
function() {
$(this).stop().animate({ paddingLeft: '20px' }, 200);
},
function() {
$(this).stop().animate({ paddingLeft: '10px' }, 200);
}
);
// Toggle with stop
$('#toggle').click(function() {
$('#box').stop(true, true).fadeToggle();
});
delay() Method
// Delay between animations
$('#box')
.fadeOut(500)
.delay(1000) // Wait 1 second
.fadeIn(500);
// Multiple delays
$('#notification')
.fadeIn(300)
.delay(3000) // Show for 3 seconds
.fadeOut(300);
// Stagger animations
$('.item').each(function(index) {
$(this)
.delay(index * 200) // Stagger by 200ms
.fadeIn(500);
});
// Delay only affects queued effects
$('#box')
.fadeOut(500)
.delay(1000) // Delays next animation
.css('color', 'red') // Happens immediately (not delayed)
.fadeIn(500);
// Use setTimeout for non-queued operations
$('#box').fadeOut(500);
setTimeout(function() {
$('#box').css('color', 'red');
}, 1500);
queue() Method
// Get queue length
var queueLength = $('#box').queue('fx').length;
console.log('Animations in queue:', queueLength);
// Add custom function to queue
$('#box')
.fadeOut(500)
.queue(function(next) {
$(this).addClass('hidden');
console.log('Added class');
next(); // Continue queue
})
.fadeIn(500);
// Multiple custom functions
$('#box')
.slideUp(500)
.queue(function(next) {
$(this).html('Updated');
next();
})
.queue(function(next) {
$(this).css('background', 'blue');
next();
})
.slideDown(500);
// Get queue contents
var queue = $('#box').queue('fx');
console.log('Queue:', queue);
// Replace entire queue
$('#box').queue('fx', []);
dequeue() Method
// Manual queue control
$('#box').queue(function() {
$(this).addClass('active');
$(this).dequeue(); // Manually advance
});
// Conditional queue advancement
$('#box')
.fadeOut(500)
.queue(function(next) {
var $this = $(this);
$.get('/api/data', function(data) {
$this.html(data);
next(); // Continue after Ajax
});
})
.fadeIn(500);
// Async operation in queue
$('#box')
.slideUp(500)
.queue(function(next) {
var $this = $(this);
setTimeout(function() {
$this.html('Delayed update');
next();
}, 2000);
})
.slideDown(500);
clearQueue() Method
// Clear remaining animations
$('#box').clearQueue();
// Stop current and clear queue
$('#box').stop().clearQueue();
// Clear specific queue
$('#box').clearQueue('fx');
// Example: Reset button
$('#reset').click(function() {
$('.animated')
.stop(true)
.clearQueue()
.css({ opacity: 1, left: 0 });
});
// Clear and restart
$('#restart').click(function() {
$('#box')
.stop(true)
.clearQueue()
.fadeOut(500)
.fadeIn(500);
});
finish() Method
// Jump to end of all animations
$('#box').finish();
// Finish vs stop(true, true)
// finish() completes ALL queued animations
// stop(true, true) only completes current animation
$('#box')
.fadeOut(1000)
.fadeIn(1000)
.slideUp(1000);
$('#finishBtn').click(function() {
$('#box').finish(); // Jumps to end of all 3 animations
});
// Example: Skip animation
$('#skip').click(function() {
$('.loading').finish();
showContent();
});
// Finish specific queue
$('#box').finish('fx');
Custom Queues
// Create custom queue
$('#box')
.queue('custom', function(next) {
$(this).css('background', 'red');
next();
})
.queue('custom', function(next) {
$(this).css('color', 'white');
next();
});
// Start custom queue
$('#box').dequeue('custom');
// Separate animation and color queues
$('#box')
.fadeOut(500) // 'fx' queue
.queue('color', function(next) {
$(this).css('background', 'blue');
next();
})
.fadeIn(500); // 'fx' queue
$('#box').dequeue('color'); // Run color queue
// Multiple independent queues
function setupQueues() {
$('#box')
.queue('slideQueue', function(next) {
$(this).slideUp(500, next);
})
.queue('fadeQueue', function(next) {
$(this).fadeOut(500, next);
});
}
$('#runSlide').click(function() {
$('#box').dequeue('slideQueue');
});
$('#runFade').click(function() {
$('#box').dequeue('fadeQueue');
});
Practical Examples
// Example 1: Notification system
function showNotification(message, duration) {
var $notification = $('<div class="notification">' + message + '</div>');
$('body').append($notification);
$notification
.hide()
.fadeIn(300)
.delay(duration || 3000)
.fadeOut(300, function() {
$(this).remove();
});
}
// Example 2: Loading sequence
function loadingSequence() {
$('.loading-dot').each(function(index) {
$(this)
.delay(index * 200)
.fadeIn(200)
.delay(800)
.fadeOut(200);
});
}
setInterval(loadingSequence, 2000);
// Example 3: Slideshow with queue
var currentSlide = 0;
var $slides = $('.slide');
function nextSlide() {
$slides.eq(currentSlide)
.fadeOut(500)
.queue(function(next) {
currentSlide = (currentSlide + 1) % $slides.length;
next();
});
$slides.eq(currentSlide).fadeIn(500);
}
setInterval(nextSlide, 3000);
// Example 4: Multi-step animation
function animateElement($element) {
$element
.animate({ left: '200px' }, 500)
.queue(function(next) {
$(this).addClass('halfway');
next();
})
.animate({ left: '400px' }, 500)
.queue(function(next) {
$(this).addClass('complete');
next();
});
}
// Example 5: Loading bar
function animateProgress($bar, percent) {
$bar
.stop(true)
.animate({ width: percent + '%' }, 1000)
.queue(function(next) {
$(this).text(percent + '%');
if (percent === 100) {
$(this).addClass('complete');
}
next();
});
}
Best Practices
// Always use stop() on interactive elements
$('.button').hover(
function() {
$(this).stop(true, true).animate({ scale: 1.1 }, 200);
},
function() {
$(this).stop(true, true).animate({ scale: 1 }, 200);
}
);
// Check queue length before adding
if ($('#box').queue('fx').length < 5) {
$('#box').fadeOut().fadeIn();
}
// Use finish() for instant state changes
$('.modal-close').click(function() {
$('.modal').finish().fadeOut();
});
// Remember to call next() or dequeue()
$('#box').queue(function(next) {
// Do something
next(); // Don't forget this!
});
// Clear queues on page transitions
$(window).on('beforeunload', function() {
$('*').stop(true).clearQueue();
});
// Use promises for complex sequences
$('#box').fadeOut(500).promise()
.then(function() {
return $.get('/api/data');
})
.then(function(data) {
$('#box').html(data).fadeIn(500);
});
Practice Tasks
- Create a staggered fade-in effect for list items.
- Build a notification that appears, waits, then disappears.
- Implement a loading animation with queue control.
- Create a multi-step animation with custom functions.
Key Takeaways
- Animations are automatically queued and run sequentially.
- Use stop() to prevent queue buildup on interactive elements.
- delay() adds pauses between queued animations.
- queue() lets you add custom functions to animation queue.
- finish() completes all queued animations instantly.
What's Next?
Next Topic: Learn Plugins.
jQuery Best Practices: Writing Better Code
Professional patterns and guidelines for jQuery development
Why Best Practices Matter
Following best practices ensures your jQuery code is fast, maintainable, secure, and follows industry standards.
Selector Best Practices
// ❌ Bad: Repeated selector queries
$('#button').addClass('active');
$('#button').text('Loading...');
$('#button').prop('disabled', true);
// ✅ Good: Cache the selector
var $button = $('#button');
$button.addClass('active')
.text('Loading...')
.prop('disabled', true);
// ❌ Bad: Expensive universal selector
$('*').hide();
// ✅ Good: Be specific
$('.content > p').hide();
// ❌ Bad: Unqualified attribute selector
$('[type="text"]').addClass('input-field');
// ✅ Good: Qualify with tag
$('input[type="text"]').addClass('input-field');
// ❌ Bad: Overly specific
$('html body div.container div.row div.col p span.highlight');
// ✅ Good: Simplified selector
$('.container .highlight');
// Provide context to limit search scope
var $container = $('#container');
$('.item', $container); // Search within container
// Or:
$container.find('.item');
DOM Manipulation Best Practices
// ❌ Bad: Multiple DOM manipulations
for (var i = 0; i < 100; i++) {
$('#list').append('<li>Item ' + i + '</li>');
}
// ✅ Good: Build HTML string first
var html = '';
for (var i = 0; i < 100; i++) {
html += '<li>Item ' + i + '</li>';
}
$('#list').append(html);
// ✅ Better: Use array join
var items = [];
for (var i = 0; i < 100; i++) {
items.push('<li>Item ' + i + '</li>');
}
$('#list').append(items.join(''));
// ✅ Best: Use DocumentFragment for complex builds
var fragment = document.createDocumentFragment();
for (var i = 0; i < 100; i++) {
var li = document.createElement('li');
li.textContent = 'Item ' + i;
fragment.appendChild(li);
}
$('#list').append(fragment);
// Detach elements during heavy manipulation
var $table = $('#my-table').detach();
// Perform many operations on $table
$table.find('td').each(function() {
$(this).addClass('processed');
});
$('#container').append($table); // Reattach once
Event Handling Best Practices
// ❌ Bad: Multiple event bindings
$('.button').on('click', function() { /* ... */ });
$('.button').on('click', function() { /* ... */ });
$('.button').on('click', function() { /* ... */ });
// ✅ Good: Use event delegation
$('#container').on('click', '.button', function() {
// Handles current and future .button elements
});
// ❌ Bad: Anonymous functions (hard to unbind)
$('#button').on('click', function() {
console.log('Clicked');
});
// ✅ Good: Named functions (can unbind)
function handleClick() {
console.log('Clicked');
}
$('#button').on('click', handleClick);
// Later: $('#button').off('click', handleClick);
// Use event namespaces for organization
$('#button').on('click.myPlugin', function() {
console.log('Plugin click');
});
// Unbind only this namespace
$('#button').off('.myPlugin');
// Prevent memory leaks - unbind when removing elements
$('#element').remove(); // ❌ Events still in memory
$('#element').off().remove(); // ✅ Clean removal
// One-time events
$('#button').one('click', function() {
console.log('Fires only once');
});
// Prevent default and stop propagation
$('#link').on('click', function(e) {
e.preventDefault(); // Don't follow link
e.stopPropagation(); // Don't bubble up
// Your code
});
Ajax Best Practices
// ✅ Good: Always handle errors
$.ajax({
url: '/api/data',
method: 'GET',
dataType: 'json',
timeout: 5000, // Set timeout
success: function(data) {
console.log('Success:', data);
},
error: function(xhr, status, error) {
console.error('Error:', error);
// Show user-friendly message
alert('Failed to load data. Please try again.');
},
complete: function() {
// Always runs (cleanup code)
$('#loading').hide();
}
});
// Use promises for cleaner code
$.ajax('/api/data')
.done(function(data) {
console.log('Success:', data);
})
.fail(function(xhr, status, error) {
console.error('Error:', error);
})
.always(function() {
$('#loading').hide();
});
// Global Ajax error handler
$(document).ajaxError(function(event, jqxhr, settings, thrownError) {
console.error('Ajax error:', thrownError);
});
// Abort previous request to prevent race conditions
var xhr = null;
function searchData(query) {
if (xhr && xhr.readyState !== 4) {
xhr.abort(); // Cancel previous request
}
xhr = $.ajax({
url: '/api/search',
data: { q: query }
});
return xhr;
}
// Cache Ajax results
var cache = {};
function getData(url) {
if (cache[url]) {
return $.Deferred().resolve(cache[url]).promise();
}
return $.get(url).done(function(data) {
cache[url] = data;
});
}
Code Organization
// ❌ Bad: Everything in document.ready
$(document).ready(function() {
// 1000 lines of code...
});
// ✅ Good: Organize into modules
var MyApp = {
init: function() {
this.setupEvents();
this.loadData();
},
setupEvents: function() {
$('#button').on('click', this.handleClick.bind(this));
},
handleClick: function() {
console.log('Button clicked');
},
loadData: function() {
$.get('/api/data', function(data) {
MyApp.renderData(data);
});
},
renderData: function(data) {
// Render logic
}
};
$(document).ready(function() {
MyApp.init();
});
// Or use revealing module pattern
var MyApp = (function() {
var config = {
apiUrl: '/api/data'
};
function init() {
setupEvents();
loadData();
}
function setupEvents() {
$('#button').on('click', handleClick);
}
function handleClick() {
console.log('Clicked');
}
function loadData() {
$.get(config.apiUrl, renderData);
}
function renderData(data) {
// Render logic
}
// Public API
return {
init: init,
loadData: loadData
};
})();
$(document).ready(function() {
MyApp.init();
});
Naming Conventions
// Prefix jQuery objects with $
var $button = $('#button');
var $items = $('.item');
var element = document.getElementById('button'); // Not jQuery
// Use descriptive names
var $submitButton = $('#submit-btn'); // ✅ Good
var $sb = $('#submit-btn'); // ❌ Bad
// Consistent event handler naming
function handleButtonClick() { } // ✅ Good
function onButtonClick() { } // ✅ Good
function btnClk() { } // ❌ Bad
// Consistent function naming
function getUserData() { } // ✅ Good: verb + noun
function showModal() { } // ✅ Good
function data() { } // ❌ Bad: unclear
Performance Tips
// Use native JavaScript when simpler
// ❌ Slower
if ($('#element').length > 0) { }
// ✅ Faster
if (document.getElementById('element')) { }
// Minimize selector complexity
$('#id .class'); // ✅ Fast
$('div#id .class'); // ❌ Unnecessary tag
// Chain methods
$('#element')
.addClass('active')
.text('Updated')
.fadeIn();
// Use find() for descendant selectors
$('#container').find('.item'); // ✅ Faster
$('#container .item'); // ❌ Slower
// Debounce high-frequency events
function debounce(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
};
}
$(window).on('resize', debounce(function() {
console.log('Resized');
}, 250));
Security Best Practices
// ❌ Bad: XSS vulnerability
var userInput = '<script>alert("XSS")</script>';
$('#output').html(userInput);
// ✅ Good: Use text() for user content
$('#output').text(userInput); // Escapes HTML
// ❌ Bad: eval-like behavior
var func = 'alert("danger")';
eval(func);
// ✅ Good: Avoid eval
function safeFunction() {
alert('Safe');
}
safeFunction();
// Validate and sanitize input
function sanitizeInput(input) {
return $('<div>').text(input).html();
}
var safeInput = sanitizeInput(userInput);
$('#output').html(safeInput);
// Use CSRF tokens for Ajax requests
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Testing Best Practices
// Separate business logic from DOM manipulation
// ❌ Bad: Hard to test
$('#calculate').on('click', function() {
var a = parseInt($('#num1').val());
var b = parseInt($('#num2').val());
var result = a + b;
$('#result').text(result);
});
// ✅ Good: Testable business logic
function add(a, b) {
return a + b;
}
$('#calculate').on('click', function() {
var a = parseInt($('#num1').val());
var b = parseInt($('#num2').val());
var result = add(a, b); // Easy to test
$('#result').text(result);
});
// Use dependency injection for testing
var MyApp = function(apiClient) {
this.apiClient = apiClient;
};
MyApp.prototype.loadData = function() {
return this.apiClient.get('/api/data');
};
// In production
var app = new MyApp($);
// In tests
var mockApi = { get: function() { /* mock */ } };
var app = new MyApp(mockApi);
Documentation
/**
* Fetches user data from the API
* @param {number} userId - The user ID to fetch
* @param {function} callback - Called with user data
* @returns {Promise} jQuery Promise object
*/
function getUserData(userId, callback) {
return $.ajax({
url: '/api/users/' + userId,
method: 'GET'
}).done(callback);
}
/**
* Validates an email address
* @param {string} email - Email to validate
* @returns {boolean} True if valid
*/
function isValidEmail(email) {
var regex = /^[^\s@@]+@@[^\s@@]+\.[^\s@@]+$/;
return regex.test(email);
}
Practice Tasks
- Refactor existing code to cache selectors and use chaining.
- Implement event delegation for dynamically added elements.
- Add error handling to all Ajax requests.
- Organize code using the module pattern.
Key Takeaways
- Cache selectors to avoid repeated DOM queries.
- Use event delegation for dynamic content.
- Always handle Ajax errors and set timeouts.
- Organize code into modules for maintainability.
- Prefix jQuery objects with $ by convention.
- Sanitize user input to prevent XSS attacks.
- Write testable code by separating business logic.
- Document complex functions with JSDoc comments.
What's Next?
Final Topic: Learn about Migration and Modernization strategies.
Debugging Tools and Techniques
Find and fix jQuery issues efficiently
Common jQuery Problems
Learn how to identify and solve common jQuery issues using browser developer tools and debugging techniques.
Console Logging
// Log jQuery selections
var $elements = $('.item');
console.log('Found elements:', $elements.length);
console.log('Elements:', $elements);
// Log element properties
$('.box').each(function(index) {
console.log('Box ' + index + ':', $(this).attr('id'), $(this).width());
});
// Log events
$('#button').click(function(e) {
console.log('Button clicked');
console.log('Event:', e);
console.log('Target:', e.target);
console.log('Current target:', e.currentTarget);
});
// Log Ajax requests
$.ajax({
url: '/api/data',
beforeSend: function() {
console.log('Request starting...');
},
success: function(data) {
console.log('Response:', data);
},
error: function(xhr, status, error) {
console.error('Ajax error:', status, error);
}
});
// Console table for arrays
var users = [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }];
console.table(users);
Check if jQuery is Loaded
// Check if jQuery loaded
if (typeof jQuery !== 'undefined') {
console.log('jQuery version:', jQuery.fn.jquery);
} else {
console.error('jQuery not loaded!');
}
// Check specific version
if (jQuery.fn.jquery >= '3.0.0') {
console.log('jQuery 3+ detected');
}
// Check $ is jQuery
if (typeof $ !== 'undefined' && $.fn) {
console.log('$ is jQuery');
} else {
console.warn('$ might not be jQuery');
}
// Safe jQuery usage
(function($) {
// $ is definitely jQuery here
$('#element').hide();
})(jQuery);
Debugging Selectors
// Check if selector finds elements
var $items = $('.item');
if ($items.length === 0) {
console.warn('No .item elements found');
} else {
console.log('Found ' + $items.length + ' items');
}
// Log what was selected
console.log('Selector .item found:', $items.get());
// Test selector validity
function testSelector(selector) {
try {
var $el = $(selector);
console.log('Selector "' + selector + '" is valid');
console.log('Found ' + $el.length + ' elements');
return $el;
} catch(e) {
console.error('Invalid selector "' + selector + '":', e);
return $();
}
}
testSelector('.box');
testSelector('#main');
testSelector('[invalid') // Invalid
// Highlight selected elements
function highlightSelection(selector) {
$(selector).css({
outline: '2px solid red',
backgroundColor: 'yellow'
});
console.log('Highlighted:', selector);
}
highlightSelection('.debug');
Event Debugging
// Check attached events (jQuery 1.8+)
var events = $._data($('#button')[0], 'events');
console.log('Events on #button:', events);
// List all click handlers
if (events && events.click) {
console.log('Click handlers:', events.click.length);
}
// Debug event delegation
$(document).on('click', '.dynamic', function(e) {
console.log('Delegated click');
console.log('Target:', e.target);
console.log('Current:', e.currentTarget);
console.log('This:', this);
});
// Trace event propagation
$('#parent').click(function() {
console.log('Parent clicked');
});
$('#child').click(function(e) {
console.log('Child clicked');
console.log('Will bubble?', !e.isPropagationStopped());
});
// Monitor all events
function monitorEvents($element, eventTypes) {
eventTypes.forEach(function(type) {
$element.on(type, function(e) {
console.log('Event:', type, 'on', this);
});
});
}
monitorEvents($('#box'), ['click', 'mouseover', 'mouseout']);
Browser DevTools
// Breakpoints with debugger
$('#button').click(function() {
debugger; // Execution will pause here
$(this).addClass('clicked');
});
// Conditional breakpoint
$('.item').each(function(index) {
if (index === 5) {
debugger; // Only break on 6th item
}
$(this).addClass('processed');
});
// Console commands (run in browser console)
// $0 - currently selected element in Elements panel
$($0).hide(); // Hide selected element
$($0).css('background', 'red'); // Change background
// $$ - document.querySelectorAll shortcut
$$('.item').forEach(function(el) {
console.log(el);
});
// Monitor function calls
monitor($); // Log all jQuery calls
monitor($.ajax); // Log all Ajax calls
unmonitor($); // Stop monitoring
// Time operations
console.time('jQuery selection');
var $items = $('.item');
console.timeEnd('jQuery selection');
console.time('Processing');
$items.each(function() {
// Process
});
console.timeEnd('Processing');
Ajax Debugging
// Log all Ajax activity
$(document).ajaxStart(function() {
console.log('Ajax request started');
});
$(document).ajaxComplete(function(event, xhr, settings) {
console.log('Ajax complete:', settings.url);
console.log('Status:', xhr.status);
console.log('Response:', xhr.responseText);
});
$(document).ajaxError(function(event, xhr, settings, error) {
console.error('Ajax error:', settings.url);
console.error('Status:', xhr.status, xhr.statusText);
console.error('Response:', xhr.responseText);
console.error('Error:', error);
});
// Debug specific request
$.ajax({
url: '/api/data',
data: { id: 123 },
beforeSend: function(xhr, settings) {
console.log('Sending request to:', settings.url);
console.log('Data:', settings.data);
console.log('Headers:', xhr.getAllResponseHeaders());
},
success: function(data, status, xhr) {
console.log('Success!');
console.log('Data:', data);
console.log('Status:', status);
console.log('XHR:', xhr);
},
error: function(xhr, status, error) {
console.error('Failed!');
console.error('Status:', xhr.status);
console.error('Status text:', xhr.statusText);
console.error('Response:', xhr.responseText);
console.error('Error:', error);
}
});
// Check pending Ajax requests
if ($.active > 0) {
console.log('Active Ajax requests:', $.active);
}
Common Issues and Solutions
// Issue 1: Element not found
// Problem:
$('#myElement').hide(); // Doesn't work
// Debug:
if ($('#myElement').length === 0) {
console.error('#myElement not found!');
console.log('Did you spell it correctly?');
console.log('Is the element in the DOM?');
console.log('Did the page finish loading?');
}
// Solution: Wait for DOM
$(document).ready(function() {
$('#myElement').hide();
});
// Issue 2: Event not firing
// Problem:
$('.dynamic-element').click(function() {
// Doesn't work for dynamically added elements
});
// Solution: Event delegation
$(document).on('click', '.dynamic-element', function() {
console.log('Delegated event works!');
});
// Issue 3: Lost context (this)
// Problem:
var obj = {
name: 'MyObject',
init: function() {
$('#button').click(this.handleClick); // 'this' is button
},
handleClick: function() {
console.log(this.name); // undefined
}
};
// Solution: Bind context
var obj = {
name: 'MyObject',
init: function() {
$('#button').click(this.handleClick.bind(this));
},
handleClick: function() {
console.log(this.name); // 'MyObject'
}
};
// Issue 4: Animation queue buildup
// Problem:
$('#box').hover(
function() { $(this).fadeOut(); },
function() { $(this).fadeIn(); }
); // Rapid hovers = many queued animations
// Solution: Stop previous animations
$('#box').hover(
function() { $(this).stop(true, true).fadeOut(); },
function() { $(this).stop(true, true).fadeIn(); }
);
// Issue 5: Memory leaks
// Problem:
setInterval(function() {
$('.item').each(function() {
// Creates new closures
});
}, 100);
// Solution: Clear intervals, unbind events
var interval = setInterval(function() {
// ...
}, 100);
// Later:
clearInterval(interval);
$('.item').off(); // Remove event handlers
jQuery Debugging Plugin
// Custom debug plugin
$.fn.debug = function() {
console.log('---Debug Info---');
console.log('Selector matched:', this.length, 'elements');
this.each(function(index) {
var $this = $(this);
console.log('Element ' + index + ':');
console.log(' Tag:', this.tagName);
console.log(' ID:', this.id || 'none');
console.log(' Classes:', this.className || 'none');
console.log(' Visible:', $this.is(':visible'));
console.log(' Width x Height:', $this.width(), 'x', $this.height());
console.log(' Position:', $this.position());
});
console.log('---End Debug---');
return this; // Chainable
};
// Usage
$('.item').debug().addClass('processed');
// Debug chain steps
$.fn.logChain = function(label) {
console.log(label + ':', this.length, 'elements');
return this;
};
$('.item')
.logChain('Initial selection')
.filter('.active')
.logChain('After filter')
.addClass('highlight')
.logChain('After addClass');
Practice Tasks
- Debug a selector that isn't finding elements.
- Trace event propagation with console logs.
- Log all Ajax requests in your application.
- Create a debug helper to inspect jQuery objects.
Key Takeaways
- Use console.log() to inspect jQuery selections and data.
- Check .length to verify elements were found.
- Use debugger statement to pause execution.
- Browser DevTools Network tab shows Ajax requests.
- Event delegation fixes issues with dynamic elements.
What's Next?
Next Topic: Learn Best Practices.
jQuery Migration & Modernization
Upgrade jQuery versions and transition to modern JavaScript
Why Migrate?
Keeping jQuery up-to-date ensures better performance, security, and compatibility. Understanding modern alternatives helps make informed decisions about when to use jQuery vs native JavaScript.
jQuery Version History
// jQuery 1.x (2006-2016)
// - Supports IE6/7/8
// - Larger file size (~84KB)
// - Legacy browser compatibility
// jQuery 2.x (2013-2016)
// - Dropped IE6/7/8 support
// - Smaller file size (~69KB)
// - Modern browser focus
// jQuery 3.x (2016-present)
// - Improved performance
// - Better error handling
// - requestAnimationFrame for animations
// - Deferred/Promise compatibility with Promises/A+
// - Deprecated .bind(), .delegate(), .unbind()
// - Removed .load(), .unload(), .error()
// Check your jQuery version
console.log('jQuery version:', jQuery.fn.jquery);
// or
console.log('jQuery version:', $.fn.jquery);
Using jQuery Migrate Plugin
<!-- Include jQuery Migrate to find issues -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://code.jquery.com/jquery-migrate-3.4.1.min.js"></script>
<!-- Open browser console to see warnings -->
// jQuery Migrate will warn about deprecated methods:
// ❌ Deprecated in jQuery 3.x
$('#element').bind('click', function() { });
// Warning: jQuery.fn.bind() is deprecated
// ✅ Use .on() instead
$('#element').on('click', function() { });
// ❌ Deprecated
$('#element').size();
// ✅ Use .length
$('#element').length;
// ❌ Deprecated
$.isArray(arr);
// ✅ Use native JavaScript
Array.isArray(arr);
Common Migration Issues
// 1. Event binding methods
// ❌ Old (deprecated)
$('#element').bind('click', handler);
$('#element').unbind('click', handler);
$('#element').delegate('.child', 'click', handler);
$('#element').undelegate('.child', 'click', handler);
// ✅ New
$('#element').on('click', handler);
$('#element').off('click', handler);
$('#element').on('click', '.child', handler);
$('#element').off('click', '.child', handler);
// 2. Ajax load/error methods
// ❌ Old (removed in jQuery 3.0)
$('#element').load('page.html');
$(document).error(handler);
// ✅ New
$('#element').load('page.html'); // Still works for HTML loading
$.ajax('url').fail(handler); // For error handling
// 3. Browser detection
// ❌ Old (removed)
if ($.browser.msie) { }
// ✅ New: Feature detection
if ('addEventListener' in window) { }
// Or use Modernizr library
// 4. Toggle event
// ❌ Old (removed)
$('#element').toggle(
function() { console.log('Odd click'); },
function() { console.log('Even click'); }
);
// ✅ New: Implement manually
var clickCount = 0;
$('#element').on('click', function() {
clickCount++;
if (clickCount % 2 === 1) {
console.log('Odd click');
} else {
console.log('Even click');
}
});
// 5. Context parameter
// ❌ Old (deprecated)
$('div', '#container');
// ✅ New
$('#container').find('div');
// Or
$('div', document.getElementById('container'));
// 6. Position in selector
// ❌ Old (different behavior in jQuery 3)
$('div:eq(2)'); // Now uses 0-based indexing consistently
// ✅ Better: Use .eq() method
$('div').eq(2);
Modern JavaScript Alternatives
// Selectors
// jQuery
$('#id');
$('.class');
$('tag');
// Native
document.getElementById('id');
document.getElementsByClassName('class');
document.getElementsByTagName('tag');
document.querySelector('#id');
document.querySelectorAll('.class');
// DOM manipulation
// jQuery
$('#element').text('Hello');
$('#element').html('<b>Hello</b>');
$('#element').append('<p>New</p>');
// Native
document.getElementById('element').textContent = 'Hello';
document.getElementById('element').innerHTML = '<b>Hello</b>';
document.getElementById('element').insertAdjacentHTML('beforeend', '<p>New</p>');
// CSS
// jQuery
$('#element').css('color', 'red');
$('#element').addClass('active');
$('#element').removeClass('active');
$('#element').toggleClass('active');
// Native
document.getElementById('element').style.color = 'red';
document.getElementById('element').classList.add('active');
document.getElementById('element').classList.remove('active');
document.getElementById('element').classList.toggle('active');
// Events
// jQuery
$('#button').on('click', function() { });
$('#button').off('click');
// Native
document.getElementById('button').addEventListener('click', function() { });
document.getElementById('button').removeEventListener('click', handler);
// Ajax
// jQuery
$.get('/api/data', function(data) {
console.log(data);
});
// Native (modern)
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
// Or with async/await
async function getData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
// Animation
// jQuery
$('#element').fadeOut();
$('#element').slideDown();
// Native (CSS transitions)
element.style.transition = 'opacity 0.5s';
element.style.opacity = '0';
// Or Web Animations API
element.animate([
{ opacity: 1 },
{ opacity: 0 }
], {
duration: 500,
fill: 'forwards'
});
When to Use jQuery vs Native
// Use jQuery when:
// 1. Working with legacy codebase already using jQuery
// 2. Need cross-browser compatibility for older browsers
// 3. Rapid prototyping with plugins
// 4. Complex DOM manipulation with chaining
// 5. Team is more familiar with jQuery
// Use Native JavaScript when:
// 1. Modern browsers only (ES6+)
// 2. Performance is critical
// 3. Minimizing dependencies/bundle size
// 4. Simple DOM queries and manipulation
// 5. Building with modern frameworks (React, Vue, Angular)
// Performance comparison
// jQuery
for (var i = 0; i < 1000; i++) {
$('.item').addClass('active'); // Query selector each time
}
// Native (faster)
var items = document.querySelectorAll('.item');
for (var i = 0; i < items.length; i++) {
items[i].classList.add('active');
}
// Or modern
document.querySelectorAll('.item').forEach(item => {
item.classList.add('active');
});
Gradual Migration Strategy
// Step 1: Update to latest jQuery 3.x
// Add jQuery Migrate plugin
// Fix all console warnings
// Step 2: Identify high-impact areas
// - Frequently called functions
// - Performance bottlenecks
// - Simple DOM operations
// Step 3: Replace simple operations first
// Before
function updateText(id, text) {
$('#' + id).text(text);
}
// After
function updateText(id, text) {
document.getElementById(id).textContent = text;
}
// Step 4: Create wrapper functions for consistency
var DOM = {
get: function(selector) {
return document.querySelector(selector);
},
getAll: function(selector) {
return document.querySelectorAll(selector);
},
on: function(selector, event, handler) {
document.querySelector(selector).addEventListener(event, handler);
},
addClass: function(element, className) {
element.classList.add(className);
}
};
// Usage
DOM.on('#button', 'click', function() {
DOM.addClass(this, 'active');
});
// Step 5: Gradually replace jQuery calls
// Keep jQuery for complex operations initially
// Replace with native JavaScript over time
// Step 6: Remove jQuery when usage < 10%
// Remove jQuery library
// Test thoroughly
Modern Build Tools
// Old: Include entire jQuery library
<script src="jquery.min.js"></script> // 84KB
// Modern: Use ES6 modules with tree-shaking
import $ from 'jquery'; // Bundle only used methods
// Or use custom jQuery build
// npm install jquery
// webpack will tree-shake unused code
// Alternatives to consider:
// - Cash: jQuery alternative (11KB)
import $ from 'cash-dom';
// - Zepto: Lightweight jQuery (10KB)
import $ from 'zepto';
// - Umbrella JS: Tiny jQuery-like (3KB)
import u from 'umbrellajs';
// Example with Cash (similar API to jQuery)
import $ from 'cash-dom';
$('.button').on('click', function() {
$(this).addClass('active');
});
$('.item').each(function() {
console.log($(this).text());
});
Testing After Migration
// Create comprehensive test suite
// Test selectors
function testSelectors() {
console.assert($('#element').length === 1, 'ID selector failed');
console.assert($('.class').length > 0, 'Class selector failed');
}
// Test event handlers
function testEvents() {
var clicked = false;
$('#button').on('click', function() {
clicked = true;
});
$('#button').trigger('click');
console.assert(clicked === true, 'Event handler failed');
}
// Test DOM manipulation
function testDOM() {
$('#element').html('<p>Test</p>');
console.assert(
$('#element p').length === 1,
'DOM manipulation failed'
);
}
// Test Ajax
function testAjax(done) {
$.get('/api/test')
.done(function(data) {
console.assert(data !== null, 'Ajax failed');
done();
})
.fail(function() {
console.error('Ajax request failed');
});
}
// Run tests
testSelectors();
testEvents();
testDOM();
testAjax(function() {
console.log('All tests complete');
});
Future of jQuery
// jQuery is still relevant for:
// - Legacy browser support
// - Existing codebases
// - Rapid prototyping
// - Plugin ecosystem
// Modern JavaScript covers most jQuery use cases:
// - querySelector/querySelectorAll for selection
// - classList for CSS manipulation
// - fetch() for Ajax
// - addEventListener for events
// - CSS animations/transitions for effects
// - Template literals for HTML building
// Recommendations:
// 1. New projects: Consider vanilla JS or modern frameworks
// 2. Existing projects: Maintain if working well
// 3. Learning: Understand both jQuery and native JS
// 4. Migration: Do it gradually, test thoroughly
// 5. Stay current: Keep jQuery updated if using it
console.log('jQuery will remain useful, but modern JS is powerful!');
Practice Tasks
- Update a jQuery 1.x project to jQuery 3.x using jQuery Migrate.
- Replace 5 jQuery methods with native JavaScript equivalents.
- Compare performance of jQuery vs native for DOM operations.
- Create a simple app without jQuery to practice vanilla JS.
Key Takeaways
- Use jQuery Migrate plugin to identify deprecated code.
- Update event binding from .bind()/.delegate() to .on().
- Modern JavaScript (ES6+) provides many jQuery alternatives.
- Migrate gradually: start with simple operations, test thoroughly.
- Consider vanilla JS or lightweight alternatives (Cash, Zepto) for new projects.
- jQuery remains useful for legacy support and rapid prototyping.
- Balance between jQuery convenience and native JavaScript performance.
Congratulations!
jQuery Course Complete! You've mastered jQuery from basics to advanced topics. Continue exploring modern JavaScript frameworks and libraries to expand your skills.
Last updated: February 2026