jQuery Plugin Development and Advanced Selector Techniques

Using Plugins

Throughout the first six chapters we examined the core components of jQuery. The library is powerful on its own, but its elegant plugin architecture allows developers to extend it with even richer functionality. Hundreds of community‑created plugins exist, ranging from tiny selector helpers to full‑fledged UI widgets. This section explains how to tap into that wealth.

We will cover:

  • Downloading and setting up a plugin
  • Calling jQuery methods that a plugin provides
  • Finding elements with custom selectors defined by plugins
  • Adding complex user‑interface behaviour with jQuery UI
  • Building mobile‑friendly features with jQuery Mobile

Working with Plugins

Using a jQuery plugin is straightforward: obtain the plugin code, reference it from you're HTML, and invoke the new functionality in your scripts. The Cycle plugin by Mike Alsup turns a static collection of elements into an interactive slideshow. It handles complex demands gracefully but hides that complexity when your needs are simple.

Downloading and Referencing the Cycle Plugin

We install plugins with the npm package manager. A package.json file declares dependencies. After initialising the file, you can add the jQuery dependency:

npm install jquery --save

Then install the Cycle plugin:

npm install jquery-cycle --save

The --save flag adds the packages to package.json. Now include them in your page:

<head>
  <meta charset="utf-8">
  <title>jQuery Slideshow</title>
  <link rel="stylesheet" href="07.css" type="text/css" />
  <script src="img/jquery.js"></script>
  <script src="img/index.js"></script>
  <script src="img/07.js"></script>
</head>

The plugin is now ready for use.

Invoking Plugin Methods

Cycle works on any group of sibling elements. We set up a list of book covers and details:

<ul id="bookList">
  <li>
    <img src="img/jq-game.jpg" alt="jQuery Game Development Essentials" />
    <div class="title">jQuery Game Development Essentials</div>
    <div class="author">Salim Arsever</div>
  </li>
  <li>
    <img src="img/jqmobile-cookbook.jpg" alt="jQuery Mobile Cookbook" />
    <div class="title">jQuery Mobile Cookbook</div>
    <div class="author">Chetan K Jain</div>
  </li>
  ...
</ul>

Light styling displays the books one after another:

Initial list of books

Apply the plugin by calling .cycle() on the container:

$(document).ready(function() {
  $('#bookList').cycle();
});

That single call transforms the list into an animated slideshow, showing one item at a time with a fade transition every four seconds:

Cycled slideshow

Well‑written plugins deliver professional results with minimal code, yet they offer many options for fine‑tuning.

Specifying Plugin Method Parameters

Passing arguments to a plugin method is the same as with native jQuery methods. Oftan you provide a single object with key‑value pairs. Cycle has over fifty options; here we adjust a few:

$(function() {
  $('#bookList').cycle({
    timeout: 2000,
    speed: 200,
    pause: true
  });
});

timeout is the millisecond delay between slides, speed the transition duration. pause: true stops the slideshow when the mouse hovers over it.

Modifying Parameter Defaults

Cycle stores its defaults in $.fn.cycle.defaults. You can change them globally:

$.fn.cycle.defaults.timeout = 10000;
$.fn.cycle.defaults.random = true;

$(function() {
  $('#bookList').cycle({
    timeout: 2000,
    speed: 200,
    pause: true
  });
});

The new timeout of 10000 is ignored because we override it in the call, but random: true takes effect, shuffling the slide order.

Other Plugin Types

Plugins can also enhance jQuery’s selection engine or provide global functions. Cycle, for instance, adds a custom selector.

Custom Selectors

Cycle can be paused and resumed with .cycle('pause') and .cycle('resume'). We add buttons to control the slideshow:

$(function() {
  var $slideshow = $('#bookList').cycle({
    timeout: 2000,
    speed: 200,
    pause: true
  });
  var $panel = $('<div/>')
    .attr('id', 'controls')
    .insertAfter($slideshow);

  $('<button/>')
    .text('Pause')
    .on('click', function() {
      $slideshow.cycle('pause');
    })
    .appendTo($panel);
  $('<button/>')
    .text('Resume')
    .on('click', function() {
      $slideshow.cycle('resume');
    })
    .appendTo($panel);
});

Cycle’s :paused custom selector lets us find all paused slideshows and resume them together:

$('button.resume-all')
  .on('click', function() {
    $('ul:paused').cycle('resume');
  });

Global Function Plugins

Some plugins introduce functions in the jQuery namespace, useful when no DOM element is involved. The Cookie plugin provides $.cookie(). We can remember that the slideshow was paused:

if ($.cookie('slidesPaused')) {
  $slideshow.cycle('pause');
}

To set and clear the cookie:

$('button.pause')
  .on('click', function() {
    $slideshow.cycle('pause');
    $.cookie('slidesPaused', 'y');
  });
$('button.resume')
  .on('click', function() {
    $('ul:paused').cycle('resume');
    $.cookie('slidesPaused', null);
  });

You can configure cookie expiration and path by passing an options object, for example { path: '/', expires: 7 }.

jQuery UI Plugin Library

While most plugins focus on a single task, jQuery UI is a comprehensive suite of related widgets and interactions. It includes draggable, sortable, resizable components, and full widgets such as buttons, accordion, datepicker, and dialog, plus a rich set of effects.

Effects

jQuery UI extends .animate() to animate colours, classes, and advanced easings.

Colour Animation

With the core effects file included, .animate() can transition properties like backgroundColor and color:

$(function() {
  $('#bookList').on('mouseenter mouseleave', 'li', function(e) {
    var $title = $(this).find('.title');
    if (e.type === 'mouseenter') {
      $title.animate({
        backgroundColor: '#eee',
        color: '#000'
      }, 1000);
    } else {
      $title.animate({
        backgroundColor: '#000',
        color: '#fff'
      }, 1000);
    }
  });
});

Animated title colours

Class Animation

The class methods gain an optional duration argument. Toggling a class becomes animated:

$('h1').on('click', function() {
  $(this).toggleClass('highlighted', 'slow');
});

Animated class toggle

Advanced Easing

jQuery UI provides easing functions like easeInExpo. You can apply them to any animation:

$('h1').on('click', function() {
  $(this).toggleClass('highlighted', 'slow', 'easeInExpo');
});

Additional Effects

Individual effect files add transitions such as shake, puff, or transfer. The .effect() method invokes them. Here we shake the Resume button if there is no paused slideshow:

$('button.resume')
  .on('click', function(e) {
    var $paused = $('ul:paused');
    if ($paused.length) {
      $paused.cycle('resume');
      $.cookie('slidesPaused', null);
    } else {
      $(e.target).effect('shake', { distance: 10 });
    }
  });

Interaction Components

The Resizable component lets users change element dimensions by dragging. Apply it with .resizable():

$(function() {
  $('#bookList').find('.title').resizable();
});

This adds a resize handle at the bottom‑right corner:

Resizable title box

To restrict resizing to the vertical direction, specify the handle:

$('#bookList').find('.title').resizable({ handles: 's' });

Vertical resize only

Widgets

jQuery UI widgets style and enhance common controls. The Button widget is simple:

$('button').button();

Icons are available from the theme framework:

$('button.pause')
  .button({ icons: { primary: 'ui-icon-pause' } });
$('button.resume')
  .button({ icons: { primary: 'ui-icon-play' } });

Buttons with icons

The Slider widget creates an interactive range input. We integrate it with the slideshow:

var $slider = $('<div/>')
  .attr('id', 'slideControl')
  .slider({
    min: 0,
    max: $('#bookList li').length - 1,
    slide: function(e, ui) {
      $('#bookList').cycle(ui.value);
    }
  })
  .appendTo($panel);

To keep the slider in sync when the slideshow advances, use Cycle’s before callback:

$('#bookList').cycle({
  before: function(slide) {
    $('#slideControl')
      .slider('value', $('#bookList li').index(slide));
  }
});

jQuery UI ThemeRoller

ThemeRoller (jqueryui.com/themeroller/) generates custom themes for widgets. After picking colours and textures, download a ZIP of stylesheets and images and reference them to instantly change the look of your interface.

jQuery Mobile Plugin Library

jQuery Mobile provides mobile‑optimised components including Ajax navigation, touch events, and responsive widgets. Its main interaction method is via HTML5 data-* attributes.

HTML5 Custom Data Attributes

jQuery Mobile scans for specific data-role and other attributes to enhance pages without JavaScript. For instance, a simple book list beecomes a mobile‑friendly page by declaring data-role="page", data-role="header", and data-role="content".

Mobile Navigation

Links between pages with data-role="page" are automatically handled by Ajax with transitions. You can also include multiple "pages" in one document using anchors.

Interactive Elements

List views are enhanced with data-role="listview" and a filter can be added with data-filter="true". Buttons naturally appear in toolbars, and a back button is added by data-add-back-btn="true".

Advanced Features

jQuery Mobile offers touch‑optimised events (tap, swipe), a ThemeRoller for mobile widgets, and integration with PhoneGap to build native apps.

Developing Plugins

When existing plugins do not fit, you can create your own. This process is similar to writing code that uses plugins. We will cover adding global functions, jQuery object methods, and UI widget‑factory plugins.

Protecting the Dollar Alias

Plugins must assume jQuery is loaded but cannot assume $ is available. Use an immediately invoked function expression (IIFE) to locally define $:

(function($) {
  // plugin code
})(jQuery);

Adding New Global Functions

Global utility methods live in the jQuery namespace. Let’s create $.addValues() to compute a sum from an array:

(function($) {
  $.addValues = function(array) {
    return array.reduce(function(total, current) {
      return parseFloat($.trim(current)) + total;
    }, 0);
  };
})(jQuery);

Now call it: $.addValues([...]).

We can also wrap multiple functions in a namespace object:

(function($) {
  $.calc = {
    total: function(arr) {
      return arr.reduce(function(sum, val) {
        return parseFloat($.trim(val)) + sum;
      }, 0);
    },
    average: function(arr) {
      if (!Array.isArray(arr)) return '';
      return $.calc.total(arr) / arr.length;
    }
  };
})(jQuery);

Usage: $.calc.total(array) and $.calc.average(array).

Adding jQuery Object Methods

To add instance methods, extend $.fn. A swapClass method that toggles two class names demonstrates the pattern:

(function($) {
  $.fn.swapClass = function(first, second) {
    return this.each(function() {
      var $el = $(this);
      if ($el.hasClass(first)) {
        $el.removeClass(first).addClass(second);
      } else if ($el.hasClass(second)) {
        $el.removeClass(second).addClass(first);
      }
    });
  };
})(jQuery);

Important: always call .each() to enforce implicit iteration and return this to support method chaining.

Flexible Method Parameters

Provide an options object with sensible defaults. A shadow plugin that overlays semitransparent clones illustrates this:

(function($) {
  $.fn.shadow = function(options) {
    var defaults = {
      copies: 5,
      opacity: 0.1,
      offset: function(idx) {
        return { x: idx, y: idx };
      }
    };
    var opts = $.extend({}, defaults, options);

    return this.each(function() {
      var $source = $(this);
      for (var i = 0; i < opts.copies; i++) {
        var pos = opts.offset(i);
        $source.clone()
          .css({
            position: 'absolute',
            left: $source.offset().left + pos.x,
            top: $source.offset().top + pos.y,
            zIndex: -1,
            opacity: opts.opacity
          })
          .appendTo('body');
      }
    });
  };
})(jQuery);

Users can customise the effect: $('h1').shadow({ copies: 3, opacity: 0.25 }). The offset option is a callback, allowing complete control of positioning.

To make defaults publicly customisable, expose them on the plugin:

$.fn.shadow.defaults = { ... };

Using the jQuery UI Widget Factory

The widget factory ($.widget) creates stateful plugins that automatically handle options merging, method chaining, and destruction. We’ll build a tooltip widget.

Define the widget with a namespace:

(function($) {
  $.widget('custom.tooltip', {
    _create: function() {
      this._box = $('<div/>')
        .addClass('custom-tooltip ui-widget ui-state-highlight ui-corner-all')
        .hide()
        .appendTo('body');
      this.element
        .addClass('custom-tooltip-trigger')
        .on('mouseenter.customtooltip', $.proxy(this._open, this))
        .on('mouseleave.customtooltip', $.proxy(this._closed, this));
    },
    _open: function() {
      if (this.options.disabled) return;
      var pos = this.element.offset();
      this._box
        .css({
          left: pos.left + this.options.offsetX,
          top: pos.top + this.element.height() + this.options.offsetY
        })
        .text(this.options.content(this.element))
        .show();
      this._trigger('open');
    },
    _closed: function() {
      this._box.hide();
      this._trigger('close');
    },
    destroy: function() {
      this._box.remove();
      this.element
        .removeClass('custom-tooltip-trigger')
        .off('.customtooltip');
      this._superApply(arguments);
    },
    options: {
      offsetX: 10,
      offsetY: 10,
      content: function(el) {
        return $(el).data('tooltip-text');
      }
    }
  });
})(jQuery);

Call $('a').tooltip() to activate. Methods like destroy, disable, enable, open, and close are available. Custom events tooltipopen and tooltipclose are triggered.

Plugin Design Recommendations

  • Protect the $ alias with an IIFE.
  • Add only one property to $.fn or $; use a namespace object.
  • Expose default options publicly ($.fn.myPlugin.defaults).
  • Return this for chaining; use .each() for implicit iteration.
  • Use callbacks for flexible behaviour.
  • Leverage the widget factory for complex, stateful components.
  • Maintain unit tests (e.g., QUnit) and version control (Git).
  • Choose a clear license (e.g., MIT).

Distributing Plugins

Proper documentation (e.g., JSDoc) and hosting on npm or GitHub make your plugin accessible. Include examples and describe every parameter and option.

Advanced Selectors and Traversing

Since jQuery 1.3, the Sizzle engine has powered $() by parsing selectors and using efficient DOM methods. Combining Sizzle with jQuery’s traversal methods provides powerful element‑finding capabilities.

We will examine dynamic table filtering, row striping, custom selectors, and performance optimisation.

Dynamic Table Filtering

Given a table of news items and topic links, we filter rows on click:

$(function() {
  $('#topics a').on('click', function(e) {
    e.preventDefault();
    var topic = $(this).text();
    $(this).addClass('selected')
      .siblings('.selected').removeClass('selected');

    $('#news tr').show();
    if (topic !== 'All') {
      $('#news tr:has(td)').filter(function() {
        return $(this).children(':nth-child(4)').text() !== topic;
      }).hide();
    }
  });
});

This hides rows whose fourth column does not match the selected topic.

Striping Table Rows

A simple striping uses :nth-child(even), but grouping two rows at a time and respecting <tbody> boundaries requires a custom filter:

$(function() {
  function stripe() {
    $('#news tbody').each(function() {
      $(this).children().has('td')
        .filter(function(i) { return (i % 4) < 2; })
        .addClass('alt');
    });
  }
  stripe();
});

When combined with filtering, re‑apply striping after topic changes, and only consider visible rows:

function stripe() {
  $('#news tr.alt').removeClass('alt');
  $('#news tbody').each(function() {
    $(this).children(':visible').has('td')
      .filter(function(i) { return (i % 4) < 2; })
      .addClass('alt');
  });
}

Custom Selector Plugins

We can extend jQuery’s expr[':'] object to define new pseudo‑classes. A :group(n) selector groups elements by index:

(function($) {
  $.extend($.expr[':'], {
    group: function(el, idx, matches) {
      var num = parseInt(matches[3], 10);
      return Number.isInteger(num) &&
        ($(el).index() - 1) % (num * 2) < num;
    }
  });
})(jQuery);

Now $('#news tr:group(3)') selects rows in alternating groups of three.

Selector Performance

Sizzle favours native .querySelectorAll() when possible. Custom jQuery selectors (like :eq, :odd) force a slower loop‑and‑test approach. Prefer standard CSS selectors and move custom filters to traversal methods. Use benchmarking tools like jsPerf to verify gains.

DOM Traversal Internals

Each jQuery object maintains a stack via .prevObject. Methods like .end() pop the stack, while .addBack() merges the current set with the previous one. This supports fluid chaining.

Writing a Traversal Plugin

A .column() method finds all cells in the same table column:

(function($) {
  $.fn.column = function() {
    var $cells = $();
    this.each(function() {
      var $td = $(this).closest('td, th');
      if ($td.length) {
        var col = $td[0].cellIndex + 1;
        var $colCells = $td.closest('table')
          .find('td, th')
          .filter(':nth-child(' + col + ')');
        $cells = $cells.add($colCells);
      }
    });
    return this.pushStack($cells);
  };
})(jQuery);

Usage: $('td').column().addClass('highlight').

Traversal Performance Tips

  • Chain methods to avoid re‑selecting elements.
  • Cache frequently used jQuery objects.

For example, store var $news = $('#news'); and reuse it.

Further Reading

See Appendix B and the official jQuery documentation for complete lists of selectors and traversal methods.

Exercises

  1. Stripe rows with classes alt and alt-2 in groups of three.
  2. Create a :containsExactly(text) custom selector.
  3. Rewrite the topic filter using :containsExactly().
  4. Write a .grandparent() traversal plugin.
  5. Benchmark .closest() vs. .parents() for finding an ancestor table with jsPerf.
  6. Benchmark :last-child, :nth-child(), .last(), and :last for the last cell in each row.

Tags: jquery plugins selectors DOM traversal jQuery UI

Posted on Sun, 14 Jun 2026 17:57:51 +0000 by zartzar