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 via CDN
<!-- 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

Document Ready Function
// 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

jQuery Syntax Structure
// 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

Full HTML Page with jQuery
<!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?

Benefits of 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

Comparison: jQuery vs 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

Main jQuery Capabilities
// 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

Important jQuery Developments
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

How jQuery Changed the Web
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

Current State and Future
// 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

Understanding the $ Symbol
// $ 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

Selector and Action Structure
// 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

Waiting for DOM to Load
// 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

Chain Multiple Methods
// 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

Understanding the Difference
// 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

Writing Clean jQuery Code
// 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, ID, and Class 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

Select by Attributes
// 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

Parent, Child, and Sibling 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

Advanced Filtering
// 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

Complex Selector Combinations
// 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

text(), html(), and val() Methods
// 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

Insert Content with append(), prepend(), etc.
// 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

Delete Elements from DOM
// 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 Content
// 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

Duplicate 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 Elements with Containers
// 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

Handle 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

Mouse Interactions
// 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

Handle Keyboard Input
// 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 Interactions
// 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

Advanced Event Handling
// 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

Handle Dynamic Elements
// 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.

Show and Hide
// 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 In and Out
// 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 and Down
// 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

Animate CSS Properties
// 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

Control Animation Flow
// 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

Complete Effect System
// 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

Performance Tips
// 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

$.ajax() Method
// 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() and $.post()
// 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() Method
// 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

Working with JSON
// 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

Submit Forms via Ajax
// 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

Handle Ajax Errors
// 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

Track All Ajax Requests
// 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

Full Ajax Application
$(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

Navigate to Parents
// 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

Navigate to Children
// 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

Navigate to Siblings
// 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 Selected Elements
// 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

Get Specific Elements from Set
// 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

Chain 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.

Understanding the Difference
// 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 and Set 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 and Set 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

Work with 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

Manage CSS Classes
// 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 and Set Form Values
// 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

Real-World Usage
// 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

css() Method
// 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, Remove, and Toggle Classes
// 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

Get and Set 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

Element Positioning
// 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

Visibility Methods
// 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

Animate CSS Properties
// 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

Basic Dimensions
// 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

Including Padding and Borders
// 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()

Element Positioning
// 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

scrollTop() and scrollLeft()
// 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

Viewport and Document Size
// 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

Find Positioned Ancestor
// 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

Real-World Usage
// 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

Smooth Position Changes
// 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 Dialog Positioning
// 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.

Common Form Events
// 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

Convert Form to Data
// 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

Validate Form Inputs
// 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/Remove 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 Data
// 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

Work with Select Elements
// 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

Full Form Handler
$(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

Iterate Over Arrays and Objects
// 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

Create New Array from Existing
// 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

Filter Array Elements
// 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

Combine 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

Check Data Types
// 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

Search Arrays
// 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 Strings
// 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 Data
// 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 Array-Like Objects
// 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

Control 'this' 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

Plugin Structure
// 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

Professional Plugin Pattern
// 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

Maintain Chainability
// 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

Method-Based Plugin
;(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

Common Plugin Examples
// 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

Tooltip Plugin
;(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 Your Plugin
// 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

Include 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

Make Elements 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

Create Drop Zones
// 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

Drag to Reorder Lists
// 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

Make Elements 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

Date Selection Widget
// 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

Modal Dialogs
// 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

Collapsible Panels
// 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

Tabbed Interface
// 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

Progress and Range Inputs
// 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

Search Suggestions
// 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

Include 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

Basic Mobile Page
<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

Multi-Page Template
<!-- 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

Touch-Friendly 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

Touch-Optimized Lists
<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

Touch-Optimized Form Elements
<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

Overlay Content
<!-- 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

Handle Touch Interactions
// 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

Customize Appearance
<!-- 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 jQuery Mobile
// 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

Avoid Repeated Selections
// 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

Optimize Selection Speed
// 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

Batch DOM Changes
// 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

Reduce Event Handlers
// 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

Limit Function Calls
// 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 vs jQuery
// 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

Efficient Effects
// 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

Prevent Memory Leaks
// 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

Load Content On Demand
// 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 Performance
// 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

Chain Multiple Methods
// 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

Navigate and Modify
// 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()

Return to Previous Selection
// 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

Attach Multiple 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

Sequential Effects
// 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 to Selection
// 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

Efficient Chaining
// 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

Real-World Chaining
// 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

Simple Callback Usage
// 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'

Access Current Element
// '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

Handle Ajax Responses
// 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

Event Handler Functions
// 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

Avoid Nested Callbacks
// 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

Advanced Callback Management
// 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

Real-World Usage
// 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

Tips and Patterns
// 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 with Promises
// 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

$.Deferred()
// 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

Pending, Resolved, Rejected
// 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 Operations
// 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

Sequential Async Operations
// 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

Wait for Animations
// 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

Catch and Handle Errors
// 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

Track Operation Progress
// 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

Real-World Application
// 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

trigger() Method
// 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

Organize Events with Namespaces
// 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

$.Event() Constructor
// 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

Real-World Examples
// 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 Communication
// 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

Event Naming and Organization
// 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

Centralized Event Management
// 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

Multi-Component Application
// 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 and Retrieve Data
// 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

Read data-* from HTML
// 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()

Understanding the Distinction
// 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

Delete Stored 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

Real-World Examples
// 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

Objects and Arrays
// 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

Dynamic Elements
// 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

Best Practices
// 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

Shopping Cart System
// 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 Selected Elements
// 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 Elements
// 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

Elements Containing Others
// 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 if Matches
// 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

Search Descendants
// 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

Real-Time Filtering
// 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

Multi-Criteria 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

Performance Optimization
// 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

Full Implementation
// 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

How Animations Queue
// 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 Animations
// 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

Add Delays Between Animations
// 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

Inspect and Modify Queue
// 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

Manually Advance Queue
// 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 All Queued Animations
// 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

Complete All Animations
// 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

Named 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

Real-World Queue Control
// 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

Queue Management Tips
// 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

Efficient Selector Usage
// ❌ 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

Optimize DOM Operations
// ❌ 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

Proper Event Management
// ❌ 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

Reliable Ajax Requests
// ✅ 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

Structure Your Code
// ❌ 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

Consistent Naming
// 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

Optimize jQuery Performance
// 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

Secure jQuery Code
// ❌ 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

Write Testable Code
// 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

Document Your Code
/**
 * 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

Basic Debugging with Console
// 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

Verify jQuery Availability
// 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

Test 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

Debug Event Handlers
// 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

Using Browser Tools
// 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

Debug Ajax Requests
// 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

Troubleshooting Guide
// 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

Create Debug Helper
// 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

Major Version Changes
// 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

Identify Deprecated Code
<!-- 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

Update Deprecated Methods
// 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

Native JavaScript Equivalents
// 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

Decision Guide
// 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-by-Step Migration
// 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

Import Only What You Need
// 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

Verify Your Changes
// 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

Looking Forward
// 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