jQuery Best Practices

written

jQuery extends the browser’s JavaScript interface, often providing thin wrappers for functions you can perform in plain JavaScript with a similar amount of code.

When jQuery is wrapping a native feature, it is doing so to provide two benefits:

  • It normalizes differences between browser JavaScript interfaces, adapting them into a single jQuery one
  • It provides an alternative paradigm for interacting with the DOM (one that is more functional in nature, and less procedural or imperative than the native JavaScript methods)

This can be thought of as normalizing the differences across browser types and providing a different way of approaching the problems jQuery solves.

When jQuery is providing its own features or implementations, it is doing so to either:

  • Polyfill or normalize features or concepts that are may not be implemented natively, particularly in older browsers.
  • To fill in gaps of functionality that do not exist at all in the underlying interface

This can be thought of as normalizing between versions of browsers and providing entirely new functionality, unavailable natively in any of them.

Having a solid understanding of when jQuery is wrapping a native feature, and when it is providing its own implementation, is the key to understanding how to write performant jQuery.

The following is not intended as a comprehensive guide, but more a useful overview and grouping of the functionality jQuery can provide. Please consult the official jQuery API docs for more information - particularly on what methods may be available for the version of jQuery you’re using.

jQuery objects

jQuery object are usually instantiated with CSS selectors (or similar), that match 0 or more DOM elements.

They’re an example of the Composite design pattern, whereby a single object may be interacted with (i.e. provide the same interface) as a collection of objects, and vice versa.

Once instantiated, they provide 3 major pieces of functionality:

  • Getters, which allow accessing attribute and property values of the first element matched.
  • Setters, which allow setting attribute and property values of all the elements matched.
  • Providing a scope to then perform further filtering or DOM traversal

jQuery object are sets

Although you often interact with jQuery objects as if they are single DOM elements, it’s important to think of them as always representing a set, rather than a single value (even if you know that set contains a single element).

Operate on the set rather than the elements

Perform actions on the set, rather than attempting to iterate over the elements individually:

1
2
3
4
5
6
7
// Bad
$( "li" ).slice( 2 ).map(function() {
  $(this).css( "background-color", "red" );
});

// Good
$( "li" ).slice( 2 ).css( "background-color", "red" );

Use set operators instead of logic ones

It’s important to understand where applying functions to sets removes the need for explicit logic. For example:

1
2
3
4
5
6
7
8
9
10
11
// Bad
var $foo = $('.foo');

if ($foo.length > 0) {
  return $foo.data('bar');
} else {
  return undefined;
}

// Good
$('.foo').data('bar');

Use the union set operator rather than repeated logic

You can union two sets of elements, (or add an individual element to an existing set) where you want to then work on them as a common set. This often simplifies logic and removes duplication.

1
$union = $( "p" ).add($("div"));

Handle (and use) empty sets

Because the jQuery objects may match no elements (an empty set), you may need to consider when this happens in your code. Empty sets return undefined for getters, and perform a no-operation for setters.

Some expressions are simpler by explicitly passing an empty set; You can do so with an empty selector:

1
$()

Selectors and the DOM

Prefer ids

Where possible, use an id selector to find the first element with that id (it’s invalid to have a HTML document with multiple elements with the same id). This delegates to native browser functions which are optimised and the fastest way to locate an element.

jQuery objects are eagerly evaluated, so ever if you don’t end up calling any methods on the jQuery object, it still performs the (relative expensive) operation of finding the matching selectors.

1
2
3
4
5
6
7
8
$foo = $('#foo');
$bar = $('#bar');

if (hideFoo) {
  $foo.hide(); // $bar isn't used, but the CSS selector has already been evaluated
} else {
  $bar.hide(); // $foo isn't used, so isn't found
}

Prefer to avoid jQuery filter extensions

If using an id to find an element is not possible (or you need to match more than one element), stick to plain CSS selectors, if you can. These are much more efficient than the syntactic sugar jQuery provides.

In general, check the docs before using any of the following, to understand if there is a more performant CSS-only alternative:

1
2
3
4
5
6
7
8
9
10
11
12
:header   :input       :hidden
:first    :button      :visible
:eq()     :password    :animated
:lt()     :checkbox
:gt()     :file
:even     :image
:odd      :radio
:last     :reset
          :selected
          :submit
          :contains
          :has

Use performant CSS selectors

A full discussion of what CSS selectors are the most performant is outside the scope of this article, but some general heuristics are as follows (each comes at the price of less specificity, which may or may not work with your document - consider your use case before applying):

These are heuristics only; the size of their effect (and even if that effect is net positive or negative) will depend on your document, the version of jQuery, web browser and device you’re using. Always measure performance yourself and avoid premature optimisation.

It may be helpful when reading the following to know that CSS selectors are applied right-to-left (i.e. starting with leaf nodes and then walking the DOM back up to the root).

Filtering based on hierarchy is expensive (particularly when it’s not an immediate child match). Try to filter only on tag, class and attribute type where possible:

1
2
3
4
5
6
7
8
9
10
11
// Bad
$('.foo .bar .baz');

// Better
$('.foo > .bar > .baz');

// Best
$('.bar > .baz');

// Or, if it's possible to change your HTML
$('.bar-baz');

Use selectors with as few components as possible (it’s less to match):

1
2
3
4
5
// Bad 
$('input.my-input[type="text"][name="my_input"]');

// Good
$('input[name="my_input"]');

Use less permissive operators where possible:

1
2
3
4
5
// Bad 
$('input[name*="y_in"]');

// Good
$('input[name="my_input"]');

Perform filtering in CSS where possible

Perform as much filtering as you can in CSS to avoid instantiating more jQuery objects in memory than you need, and performing the equivalent in the relatively slow JavaScript:

1
2
3
4
5
// Bad 
$('.foo').first();

// Good
$('.foo:first-of-type');

Search only the part of the document you need

Where possible, search only the descendants of a known ancestor, rather than the entire document:

1
2
3
4
5
6
7
8
9
10
// Bad
$("span")

// Good
$( "div.foo" ).click(function() {
  $( "span", this ).addClass( "bar" );
});

// Or
$( "div.foo" ).find('span');

Reuse references to ancestors

Often you will already have a ancestor element in scope. Use it to restrict further searching:

1
2
3
4
5
6
7
// Bad
$( "div.foo" ).addClass('bar');
$( "div.foo baz" ).addClass('zap');

// Good
$foo = $( "div.foo" ).addClass('bar');
$foo.find('baz').addClass('zap');

Prefer selection over traversal

You can think of traversal as jumping to a convenient nearby element and then performing a smaller hop to the element you’re actually interested in. Selection, however, is jumping straight to the element you’re interested in.

jQuery provides methods for both, and they both have their place (traversal is particularly useful for moving between sibling elements, for example), but always carefully consider selection before resorting to traversal:

1
2
3
4
5
6
7
8
9
10
11
// Bad
$('.foo').children(function() {
  if ($(this).attr('class') == 'baz') {
     doSomething(this);
  }
});

// Good
$('.foo > .baz').each(function (element) {
  doSomething($(element));
});

Getters and Setters

jQuery getters and setters are both called on sets. However, the getter returns the matching attribute of only the first element, while setters set the value of the attributes of all matching elements. To get the attributes of all matched elements, you must use each() or map():

1
2
3
4
5
6
7
8
9
10
// Return the name attribute of the first matched element
$('.foo').attr('name');

// Return the name attribute of all matched element
$('.foo').map(function() {
  return $(this).attr('name');
});

// Set the name of all matching elements
$('.foo').attr('name', 'baz');

Setter methods are designed to be chained, and return the jQuery object, allowing several method calls to be chained in somewhat of a Builder design pattern. Getters, on the other hand, return the value of the attribute you’re requesting.

If you want to set an attribute based on logic at call-time (or the element’s current value), you can pass a function to setter methods to return the desired new value, which is called once for each HTML element in the matching set. The function is passed the element’s index in the set as the first argument, and its current value as the second. It is also bound to the current element, so this refers to it.

jQuery treats HTML elements as having four different attribute types, which you must get and set differently (particularly in later versions of jQuery):

  • Properties: Values that affect the dynamic state of a DOM element without changing the serialized HTML attribute. They often record the result of the user interacting with the HTML document, and are oriented around recording some sort of state.
  • Value: These are a special type of property, that apply to some elements (particularly form elements) which have values associated with them.
  • Attributes: Can be thought of as everything else, but are more specifically attributes of the element, and are part of the HTML document.
  • Data attributes: These are a special type of pseudo-attribute that contain arbitrary custom data associated with the element, bootstrapped from the values stored as part of the HTML document as data-* HTML attributes (although they can be added to or modified in jQuery afterwards without being necessary written back to the DOM).

Examples of properties:

1
2
3
4
5
6
7
8
9
10
11
12
tagName
nodeName
nodeType
ownerDocument

selectedIndex
defaultChecked
defaultSelected
checked
selected
disabled
onclick

To get and set properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Get property of first matched element
$('.foo').prop('bar');

// Set property on all matching elements
$('.foo').prop('bar', 'baz');

// Bulk property assignment 
$('.foo').prop({ bar: 'baz', bawp: 'woop' });

// Set using function
$('.foo').prop('bar', function(index, property) {
  return property + ' again';
});

// Remove property from all matched elements
$('.foo').removeProp('bar');

To get and set values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Get current value of first matching element
$('.foo').val();

// Set value on all matching elements
$('.foo').val('bar'); // Must manually trigger change event with $('.foo').trigger("change")

// Bulk set - matching elements a current value that matches one of the elements 
// in the array are checked/selected (those that don't will be unchecked/unselected)               
$('.foo').val(['Yes', 'OK']);

// Set using function
$('.foo').val('bar', function(index, value) {
  return !value; // return undefined if you want to not change value  
});

To get and set attributes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Get attribute of first matched element
$('.foo').attr('bar');

// Set attribute on all matching elements
$('.foo').attr('bar', 'baz');

// Bulk attribute assignment 
$('.foo').attr({ bar: 'baz', bawp: 'woop' });

// Set using function
$('.foo').attr('bar', function(index, attribute) {
  return attribute + ' again';
});

// Remove
$('.foo').removeAttr('bar');

To get and set data:

  • When getting and setting values as data, it is converted to a string and then back to a JavaScript object, unless that conversion result in rounding errors (floats, for example, are left as strings).
1
2
3
4
5
6
7
8
9
10
11
// Return all data values associated with first matching element
$('.foo').data();

// Return specific data value associated with first matching element
$('.foo').data('bar');

// Set data of all matching elements
$('.foo').data('bar', 'baz');

// Remove
$('.foo').removeData('bar');

Styling

Working with classes

A common way to apply styling is to manipulate a DOM element’s classes (thereby changing the style rules that match the element).

To query whether an element currently has a class:

1
$('.foo').hasClass('bar');

Modifying an element’s classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Add a class to all matching elements
$('.foo').addClass('bar');

// Add multiple classes
$('.foo').addClass('bar baz');
$('.foo').addClass(['bar', 'baz']);

// Add class based on current classes
$('.foo').addClass(function(index, currentClasses) {
  return 'newClass';
});

// Toggle a class
$('.foo').toggleClass('bar');
$('.foo').toggleClass('bar baz');

// Toggle class based on a a value
$('.foo').toggleClass('bar', shouldAddClass);

$('.foo').toggleClass(function(index, currentValue, isToggled) {
  // Return class to consider toggling
});

// Replace all classes
$('.foo').attr('class', 'new classes');

// Remove specific class
$('.foo').removeClass('bar');
$('.foo').removeClass('bar baz');
$('.foo').removeClass(['bar', 'baz']);

// Remove all classes
$('.foo').removeClass();

$('.foo').removeClass(function(index, currentvalue) {
  // Return class to remove
});

Calculated values, dimensions and positions

Sometimes it’s not possible to achieve the styling or functionality you need through CSS alone; this is where jQuery’s dimensions methods come in handy. They normalize the differences between quantities reported by browsers into a unified interface.

CSS values

To query the CSS values of elements (property names are camelcased):

1
2
3
4
$('.foo').css('borderTopWidth');

// Bulk query (returns results as an array)
$('.foo').css(['borderTopWidth', 'borderRightWidth']);

Dimensions

jQuery’s dimension methods return unit-less pixel values, which may be not be whole numbers.

  • They return the same values, regardless of the CSS box-sizing
  • The values are incorrect if the user has zoomed in or out
  • The values may be incorrect if the parent element is hidden (jQuery temporarily shows, measures and then hides the element - which can have a large impact on performance)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Element dimensions only
$('.foo').height();
$('.foo').width();

// Dimensions + padding
$('.foo').innerHeight();
$('.foo').innerWidth();

// inner* + border
$('.foo').outerHeight();
$('.foo').outerWidth();

// outer* + margin
$('.foo').outerHeight(true);
$('.foo').outerWidth(true);

// Get the dimensions of the browser viewport
$(window).height();

// Get the dimensions of the document
$(document).height();

Positions

Offset

An element’s offset is the absolute coordinate of it’s border box relative to the document (as an object with left and top attributes).

  • Does not support hidden elements
  • Does not include the margin of the html tag, if appropriate
  • Values are incorrect if the page is zoomed in or out
1
2
3
4
5
6
7
8
9
// Get offset of first matching element
$('.foo').offset();

// Set offset of all matching elements
$('.foo').offset({ top: 10, left: 30 });

$('.foo').offset(function(index, currentCoordinates) {
  // return new coordinates
});

Relative positions

To get the position of an element’s padding box to its parent:

1
2
3
4
$('.foo').position();

// Get the closest ancestor that is positioned (relative, absolute or fixed):
$('.foo').offsetParent();

Scroll position

An element’s scroll position is the number of pixels hidden from view, above the scrollable area.

  • If the element is not scrolled, the value is 0
1
2
3
4
5
6
7
// Get the scroll position of the first matching element
$('.foo').scrollTop();
$('.foo').scrollLeft();

// Set the scroll position of all matching element
$('.foo').scrollTop(10);
$('.foo').scrollLeft(10);

DOM Mutation

JQuery provides the ability to not only filter and locate elements in the DOM, but also to mutate it.

Parsing HTML strings into HTML elements

Parse a HTML string into an array of DOM nodes:

  • Doesn’t remove any leading or trailing whitespace (use $.trim() first, for that)
1
$.parse(HTMLString)

Use for an iframe:

  • Default for before jQuery 3.0: context is the current document (or if you pass undefined or null);
  • Default for jQuery 3.0: A new document is used. (scripts now don’t automatically run on the current document and you have the chance to iterate over the HTML and remove anything harmful before it’s added into the current document.)
1
$.parse(HTMLString, context)

Parse and keep the script tags:

  • Must explicitly opt in
  • Not true of most other methods in jQuery - they’ll keep script tags in and likely execute them
1
$.parse(HTMLString, context, true)

Replacing Elements

Outside replace (replace entirely)

Use replaceAll when you already have the replacement as a jQuery object, and replaceWith when you already have the target to be replaced, as a jQuery object:

  • When replaceWith is passed a jQuery object that matches a single element, it has the effect of moving that element from its current position in the DOM.
1
2
3
4
5
6
7
$('<div class="foo" />').replaceAll($('.target'));

$('.target').replaceWith('<div class="foo" />');

$('.target').replaceWith(function(index, oldHTML) {
  return '<div class="foo" />';
});

Inside replace (replace contents only)

1
2
3
4
5
// Replace contents with HTML (prefer text where you can, for security)
$('.foo').html('<p>Foo</p>');

// Replace contents with text (will escape HTML)
$('.foo').text('Foo');

Removing elements

Removing an element’s parent

1
2
3
4
5
// Remove parents of all matched elements
$('.foo').unwrap();

// Remove parents of all matched elements, if that parent matches a selector
$('.foo').unwrap('.bar');

Outside remove (remove entirely)

1
2
3
4
5
6
// Remove all matched elements from the DOM, but keep data and event handlers
// Useful for re-attaching in the future
$detached = $('.foo').detach();

// Remove all matched elements, including associated data and event handlers
$('.foo').remove();

Inside remove (remove contents)

1
2
// Remove all contents and children (including associated data and event handlers) from all matched elements
$('.foo').empty();

Appending elements

Outside append (append siblings)

Use insertAfter when you already have the HTML to append as a jQuery object, and after when you already have the target to append to, as a jQuery object:

  • When after is passed a jQuery object that matches a single element, it has the effect of moving that element from its current position in the DOM.
1
2
3
4
5
6
7
$('<div class="foo" />').insertAfter($('.target'));

$('.target').after('<div class="foo" />');

$('.target').after(function(index, oldHTML) {
  return '<div class="foo" />';
});

Inside append (append children)

Use appendTo when you already have the HTML to append as a jQuery object, and append when you already have the target to append to, as a jQuery object:

  • When append is passed a jQuery object that matches a single element, it has the effect of moving that element from its current position in the DOM.
1
2
3
4
5
6
7
$('<div class="foo" />').appendTo($('.target'));

$('.target').append('<div class="foo" />');

$('.target').append(function(index, oldHTML) {
  return '<div class="foo" />';
});

Prepending elements

Outside prepend (prepend siblings)

Use insertBefore when you already have the HTML to prepend as a jQuery object, and before when you already have the target to prepend to, as a jQuery object:

  • When before is passed a jQuery object that matches a single element, it has the effect of moving that element from its current position in the DOM.
1
2
3
4
5
6
7
$('<div class="foo" />').insertBefore($('.target'));

$('.target').before('<div class="foo" />');

$('.target').before(function(index, oldHTML) {
  return '<div class="foo" />';
});

Inside prepend (prepend children)

Use prependTo when you already have the HTML to prepend as a jQuery object, and prepend when you already have the target to prepend to, as a jQuery object:

  • When prepend is passed a jQuery object that matches a single element, it has the effect of moving that element from its current position in the DOM.
1
2
3
4
5
6
7
$('<div class="foo" />').prependTo($('.target'));

$('.target').prepend('<div class="foo" />');

$('.target').prepend(function(index, oldHTML) {
  return '<div class="foo" />';
});

Wrapping elements

Outside wrap (Adding parent)

To wrap matching elements in a selector, HTML string, element or jQuery Object (first matching element is used):

1
2
3
4
5
6
7
8
9
// Wrap each matching elements in HTML
$('.foo').wrap('<div class="bar"></div>');

$('.foo').wrap(function(index) {
  return "<div class='" + $( this ).text() + "'></div>";
});

// Wrap all matching elements with the SAME parent
$('li.foo').wrapAll('<ul></ul>');

Inside wrap (Adding children to wrap all current children)

1
2
// Wrap the contents of each matching elements in HTML
$('.foo').wrapInner('<div class="bar"></div>');

Animation

If you need to make animated changes, (which can’t be achieved with CSS or manipulating the DOM), jQuery provides a series of functions, each with the same signature:

1
2
3
4
5
6
$('.foo').methodName();
$('.foo').methodName(duration);
$('.foo').methodName(duration, callback);
$('.foo').methodName(duration, easing, callback);
$('.foo').methodName(duration, options);
$('.foo').methodName(options);

These functions work by animating width, height or opacity (sometimes all 3) over a duration period, and then setting the display value to none (when hiding), or to restore it its previous value (when showing).

To animate all 3 properties:

1
2
3
4
5
6
7
8
9
// Hide and show immediately
$('.foo').hide();
$('.foo').show();
$('.foo').toggle();

// Hide and show over a duration in ms
$('.foo').hide(100);
$('.foo').show(100);
$('.foo').toggle(100);

To animate the opacity property (causing the elements to appear to fade in and out):

1
2
3
4
5
6
7
// Does not unhide the element; can specify final opacity (Default: 100)
$('.foo').fadeIn();

// Unhides a hidden element and fades in or out (always to 100% opacity)
$('.foo').fadeTo();
$('.foo').fadeOut();
$('.foo').fadeToggle();

To animate the height property (causing the content below to appear to slide up):

1
2
3
$('.foo').slideUp();
$('.foo').slideDown();
$('.foo').slideToggle();

These methods are actually convenience wrappers around a much more powerful animation function that allows you to animate any numeric style property:

1
2
3
$('.foo').animate({
  opacity: 0.25,
}, 5000, callback);

The full list of animation options are detailed here.

Events and user interaction

The event object

To understand how jQuery handles events, it’s important to have a familiarity with the jQuery event object. It’s a superset (or extension) of those provided by browsers natively, with differences in behaviour normalized away. The native browser event is always available on the jQuery one as event.originalEvent.

It has the following properties (values are populated, depending on the type of event being triggered):

Elements
target
DOM element that initiated the event.
  • useful to compare event.target to this in order to determine if the event is being handled due to event bubbling

currentTarget
Current DOM element within the event bubbling phase.
  • Typically equal to this in handler function
delegateTarget
Element where the currently-called jQuery event handler was attached
  • used, for example, to identify and remove event handlers at the delegation point.

$( ".box" ).on( "click", "button", function( event ) {
  $( event.delegateTarget ).css( "background-color", "red" );
});
Propagation
isDefaultPrevented()
Whether event.preventDefault() was ever called on this event object.
isImmediatePropagationStopped()
Whether event.stopImmediatePropagation() was ever called on this event object.
isPropagationStopped()
Whether event.stopPropagation() was ever called on this event object.
Keyboard
metaKey
Whether the META key was pressed when the event fired.
  • Mac: Command Key, Windows: Windows key
which
For key or mouse events, this property indicates the specific key or button that was pressed.
  • normalizes event.keyCode and event.charCode.
  • normalizes button presses (mousedown and mouseup events), reporting 1 for left button, 2 for middle, and 3 for right. Use event.which instead of event.button.
Mouse
pageX
mouse position relative to the left edge of the document.
pageY
mouse position relative to the top edge of the document.
relatedTarget
Other DOM element involved in the event, if any.
  • For mouseout, indicates the element being entered; for mouseover, indicates the element being exited.
General
timeStamp
Time in milliseconds
type
Type of event
Custom & Message pasing
data
Optional object of data passed to an event method when the current executing handler is bound.
result
Last value returned by an event handler that was triggered by this event, unless the value was undefined.
  • useful for getting previous return values of custom events.

$( "button" ).click(function( event ) {
  return "hey";
});
$( "button" ).click(function( event ) {
  $( "p" ).html( event.result );
});
namespace
Namespace specified when the event was triggered.

Browser events

Document ready

To attach behaviour as soon as the DOM becomes safe to manipulate:

  • Good time to perform tasks needed before the user views or interacts with the page, such as adding event handlers and initializing plugins
  • Most browsers provide the equivalent DOMContentLoaded event, however if you bind handlers to this event after it’s already been triggered, the handlers are never invoked. jQuery’s method will call any handlers bound after the event, immediately, so you don’t have to worry about your setup code being skipped.
1
2
3
$(function() {
    // Do something
});

To wait for all assets (including images) on the page to load:

1
2
3
$(window).on('load', function() {
    // Do something
});

Window resizing

1
2
3
4
5
6
7
8
9
$( window ).resize(function() {
  $( "#log" ).append( "<div>Handler for .resize() called.</div>" );
});

// Detach
$(window).off( "resize" );

// Manually trigger event
$( window ).resize();

Window and elements scroll

1
2
3
4
5
6
7
8
9
$( "#target" ).scroll(function() {
  $( "#log" ).append( "<div>Handler for .scroll() called.</div>" );
});

// Detach
$(window).off("scroll");

// Manually trigger event
$( "#target" ).scroll();

Binding event handlers

In order to bind events to elements, they must exist in the DOM at the time. This is why you should always wait for the document to be ready before attempting to bind handlers:

1
2
3
$(function() {
  $('.foo').on('click', handler);
});

You also need to bind you event handlers to elements above any content that may be modified after the initial document is ready (this includes DOM mutations you perform yourself, or content you may load via AJAX), and use event delegation to call the correct handler.

1
2
3
4
// A table that may have its rows modified after the DOM has loaded
$( "#dataTable tbody" ).on( "click", "tr", function() {
  console.log( $( this ).text() );
});

In generally, event handlers (particularly delegated ones) should be bound to as small and as few a parts of the document as possible - particularly for events that are triggered many times a second like scroll and mousemove. You want to avoid listening to events for other parts of the document that you don’t end up handling.

Event bubbling and event delegation

Browser events bubble, or propagate, from the deepest, innermost element in the document where they occur (the event target), up to the root document. jQuery uses this fact (and actually simulates it in some cases) to provide event delegation: whereby you can bind an event handler for an element to its ancestor, by providing a selector to match events that originate from the event target.

Using event delegation makes it easier to bind event handlers to elements that may change after the DOM has loaded (see above); it also makes for more performant code in circumstances where binding an event handler to a single ancestor saves you having to do so for many individual elements:

1
2
3
4
// Bind to a (single) table body, for a table that may have 1000s of rows
$( "#dataTable tbody" ).on( "click", "tr", function() {
  console.log( $( this ).text() );
});
Binding Type

Descendant Selector used
Value of this is element
Handler is called when event
Directly occurs on target
Occurs on descendants
Direct
No
Where the handler was attached
Yes
Yes
Delegated
Yes
Matching selector (not where event occurred)
No
Yes

Preventing propagation and default behaviour

Sometimes it’s preferable to prevent event propagation (so an ancestor element doesn’t try to handle an event a descendant has already handled, for example). This is prevented with .stopPropagation().

Some elements also have a default behaviour the browser invokes when an event occurs (e.g. a link is followed when clicked on). This can be prevented by calling .preventDefault() on the event object.

Preventing the default behaviour and stopping propagation will not prevent any other jQuery handlers bound to that element from running. Use .spotImmediatePropagation() to prevent those from running.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Returning false from a handler is equivalent to preventing the default and stopping propagation
$( "#foo" ).on( "click", function() {
  // ...
  return false;
});

// Shorthand for function(){ return false; }
$( "#foo" ).on( "click", false);

// Cancel only the default action
$( "form" ).on( "submit", function( event ) {
  event.preventDefault();
});

// Only prevent an action from bubbling up
$( "form" ).on( "submit", function( event ) {
  event.stopPropagation();
});

// Prevent any other handlers that may have been bound to this event and element
$( "form" ).on( "submit", function( event ) {
  event.spotImmediatePropagation();
});

Events that don’t bubble

focus and blur events are specified by the W3C to not bubble. However, jQuery defines custom cross-browser focusin and focusout events that do bubble. When focus and blur are used to attach delegated event handlers, jQuery maps them internally to focusin and focusout.

load, scroll, and error events also do not bubble.

In IE 8 and lower, the paste and reset events do not bubble.

Manually triggering events

You can manually simulate the triggering of events, complete with event bubbling.

  • For plain objects and DOM objects other than window, if a triggered event name matches the name of a property on the object, jQuery will attempt to invoke the property as a method (if no event handler calls event.preventDefault()).
  • If an event name matches the name of a property on the object, prefixed by on, jQuery will attempt to invoke that property as a method.
1
2
// Trigger event for all matching elements
$('.foo').trigger('click');

If you don’t want to trigger any native property with a mtching name, or have event bubbling:

  • Triggers any handlers attached through jQuery and any native methods attached to the element with the same name, prefixed by on.
  • Events do not bubble up the DOM hierarchy
  • Returns result of the handler, rather than the jQuery object for further chaining
  • Won’t call eventName() on the element (e.g triggerHandler('submit') won’t call .submit())
1
2
// Trigger jQuery handlers (only) for the *first* matched element
$('.foo').triggerHandler('click');

Removing event handlers

Automatically unbind a handler after its first invocation:

  • Takes same arguments as on()
1
2
3
$('.foo').one('click', function() {
  // Do something the first time an event occurs
});

Removing handlers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Remove all handlers (for any event) attached to all matching elements
$('.foo').off();

// Remove handlers only for a particular event
$('.foo').off('click');

// Remove all handlers within a custom namespace
$('.foo').off('click.myPlugin');

// Remove delegated handlers
$('.foo').off('click', 'tr');

// Remove all delegated handlers, leaving non-delegated ones
$('.foo').off('click', '**');

// Remove specific handler function
$('.foo').off('click', myFunction);

Custom events

If a handler is bound to a string value that is not the name of a native DOM event, then it is considered a custom event. These are never called by the browser, but may be triggered manually.

Namespaced events allow triggering or unbinding events without affecting the native ones (or other custom ones). If a event type string contains a period, it’s a namespaced event (they should contain only letters and digits).

1
2
3
4
5
6
7
// Event type: click; namespaces: myPlugin, simple
$('.foo').on('click.myPlugin.simple', handler )

// Can be removed with either (like CSS classes, namespaced events are not hierarchical; 
// only one of the namespaces needs to match.) 
$('.foo').off('click.myPlugin');
$('.foo').off('click.simple');

Passing custom data to event handlers

Passing data at bind-time:

1
2
3
4
5
$('.foo').on('click', {
  bar: 'baz'
}, function (event) {
  // Access as event.data.bar;
})

Passing at trigger-time:

1
2
3
4
5
$('.foo').on('click', function(event, arg1, arg2) {
  // Access arg1 and arg3, etc (Note the array elements are split out into separate arguments)
});

$('.foo').trigger('on', ['bar', 'baz']);

Event shorthands

jQuery provides a number of shorthands for common events. They’re equivalent to calling the longer .on(eventName, ...) form, and accept the same arguments (aside from the event name being defined for you).

1
$('.foo').mousedown(handler) // equivelent of $('.foo').on('mousedown', handler)

Mouse events

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// When a mouse pointer is over an element, and the button is pressed
$('.foo').mousedown(handler);

// When a mouse pointer is over an element, and the button is released
$('.foo').mouseup(handler);

// When a mouse pointer is over an element, and the button is both pressed and released without exiting it
$('.foo').click(handler);

// When a mouse pointer is over an element, and the button is both pressed and released 
// (twice) without exiting it (it's not recommended to bind to both click and doubclick; 
// browsers differ in the order they report these events)
// Double-click sensitivity depends on OS, browser and user configuration 
$('.foo').dbclick(handler);

// When a mouse pointer is over an element, and the right button is both pressed and 
// released without exiting it. Triggered before the context menu is displayed.
$('.foo').contextmenu(handler);

// When mouse pointer enters an element (propietary to IE, but jQuery makes it available
// in other browsers)
// Only triggers when pointer enters element it's bound to - not its descendants
$('.foo').mouseenter(handler);

// Like mouseenter, but also gets triggered when mouse enters descendants of the 
// element and bubbles up. i.e. Event can be triggered when re-entering a child 
// without having re-entered the target element
$('.foo').mouseover(handler);

// When mouse moves within an element
// jQuery normalizes the pageX and pageY properties on the event object between browsers
$('.foo').mousemove(handler);

// Complement of mouseenter; when the mouse leaves an element (notes on mouseleave apply 
// here)
$('.foo').mouseleave(handler);

// Complement of mouseover; when the mouse leaves an element (notes on mousover apply 
// here)
$('.foo').mouseout(handler);

// When a mouse enters or leaves an element
$('.foo').hover(handler);
$( selector ).hover( handlerIn, handlerOut );

Mouse event objects have the following values for which:

  • 1 For left button
  • 2 For right button
  • 3 For middle button

Keyboard events

Keyboard events are sent to the element that is currently in focus (focusable elements vary between browsers).

Global keyboard events can be captured by attaching to document.

In order to determine which key was the subject of an event, jQuery normalizes this information into the which event property, which contains a key code. For keydown and keyup events, its value does not change for uppercase letters. For the keypress event, it’s value is different for uppercase letters. E.g. a lowercase “a” will be reported as 65 by keydown and keyup, but as 97 by keypress. An uppercase “A” is reported as 65 by all events.


When
Triggered by modifier & non-printing keys
.which code indicates
Covered by specification
Keydown
User presses key (every time OS repeats key)
Yes
key pressed
Yes
Keypress
Browser registers keyboard input
No

Character entered
No
Keyup
User releases key (every time OS repeats key)
Yes
key pressed
Yes
1
2
3
$('.foo').keydown() // shortcut for .on('keydown', ...);
$('.foo').keypress() // shortcut for .on('keypress', ...);
$('.foo').keyup() // shortcut for .on('keyup', ...);

Focus events

Historically browsers limited focusable elements to some form elements, but modern browsers extend this to other elements that have a tabindex property set.

1
2
3
4
5
6
7
8
9
10
11
// When any of the matching elements are focused
$('.foo').focus() // shorthand for .on('focus', ...)

// Include when any of an elements descendants are focused 
$('.foo').focusin() // shorthand for .on('focusin', ...)

// When any of the matching elements lose focus
$('.foo').blur() // shorthand for .on('blur', ...)

// Include when any of an elements descendants lose focus 
$('.foo').focusout() // shorthand for .on('focusout', ...)

Change events

The change events are limited to input, textarea and select elements. The event is fired immediately when the user makes a selection for select boxes, checkboxes and radio buttons; the event is deffered for other element types until it loses focus.

Changing the value using JavaScript (e.g. using .val()) doesn’t fire the event.

1
$('.foo').change() // shorthand for .on('change', ...)

Selection events

Selection can occur for input of type text and textarea elements, when the user selects some of the element’s input. The method for retrieving the selected text varies between browsers, but there are jQuery plugins that help normalize it.

1
$('.foo').select() // shorthand for .on('select', ...)

Form submission events

Submit events only occur on form elements and occur when clicking a input or button of type submit or image, or by pressing Enter when certain form elements are focused (although this varies between browsers). The event occurs before the actual submission, so you have a chance to prevent it.

1
$('.foo').submit() // shorthand for .on('submit', ...)

AJAX

Serializing data for requests

1
2
3
4
5
6
// Create URL query string from object (recursive as of jQuery 1.4)
// Not suggested for complex or deeply nested objects
$.param(obj);

// With deep serialization disabled
$.param(obj, true);

It’s possible serialize the values from a form as a JSON string. Although jQuery can act on selected individual form controls, it’s easier so select the form tag itself. jQuery will then use the W3C rules for successful controls to determine which elements to include. Namely:

  • Submit button values are not serialized when the form was not submitted using a button
  • Form elements must have a name attribute to be included
  • Checkbox and radio button values are included only if they are checked
  • Form field data is not serialized
1
2
3
// Serialize into a JSON string
$('form').serializeArray();     // Preferred
$(':input').serializeArray();   // Alternative if you need or want to select inputs directly 

Similarly, for serializing form data as URL-encode strings:

1
2
3
4
5
// Serialize into URL-encoded string:
$('form').on('submit', function( event ) {
  event.preventDefault();
  console.log($( this ).serialize());
});

jqXHR and Promise objects

All AJAX methods return jqXHR objects, which implement the Promise interface:


(Deprecated) name (removed in jQuery 3.0)
Equivalent to callback
done(function( data, textStatus, jqXHR ) {});
success
success
fail(function( jqXHR, textStatus, errorThrown ) {});
error
error
always(function( data|jqXHR, textStatus, jqXHR|errorThrown ) { })
complete
complete

then(function( data, textStatus, jqXHR ) {}, function( jqXHR, textStatus, errorThrown ) {});

success and error

jqXHR objects can be used for setting handlers at the the time the object is instantiated, or at a later point.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Assign handlers immediately after making the request, and remember the jqxhr object for this request
var jqxhr = $.get( "example.php", function() {
  alert( "success" );
})
  .done(function() {
    alert( "second success" );
  })
  .fail(function() {
    alert( "error" );
  })
  .always(function() {
    alert( "finished" );
  });

// Perform other work here ...

// Set another completion function for the request above
jqxhr.always(function() {
  alert( "second finished" );
});

AJAX shorthands

jQuery provides a number of convenience methods that wrap the more generalized ajax method:

Method
Equivalent
$.get( url, data, success, dataType)
$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

$.get( "ajax/test.html", function( data ) {
  $( ".result" ).html( data );
  alert( "Load was performed." );
});
$.getJSON(url, data, success);
$.ajax({
  dataType: "json",
  url: url,
  data: data,
  success: success
});
$.getScript(url, success);
$.ajax({
  url: url,
  dataType: "script",
  success: success
});
$.post( url, data, success, dataType)
$.ajax({
  type: "POST",
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

Loading HTML into the DOM

jQuery also provides a helper for loading HTML documents over the AJAX and mounting their response into the DOM.

  • Ajax request will not be sent if no element is matched by the selector
1
2
3
4
5
6
7
8
9
// Load content and mount its entire response into the DOM
//
// WARNING: the content is passed to .html() PRIOR to scripts being removed. This EXECUTES the script 
// blocks before they are discarded.
$( "#result" ).load( "ajax/test.html" );

// Only mount a subset of the response into the DOM (using a selector to determine which subset)
// This form strips out script tags before the DOM is updated (so they're not executed)
$( "#result" ).load( "ajax/test.html #container" );

AJAX options

This table explains the complete set of AJAX options used with the generalised $.ajax() function:

Settings
Default
Description
Request Headers
accepts
Depends on dataType
Sets the Accept header
  • Key/value pairs that map dataType to MIME type
contentType
application/x-www-form-urlencoded; charset=UTF-8
As of jQuery 1.6 you can pass false to tell jQuery to not set any content type header

For cross-domain requests, setting the content type to anything other than application/x-www-form-urlencoded, multipart/form-data, or text/plain will trigger the browser to send a preflight OPTIONS request to the server
mimeType

mime type to override the XHR mime type
headers

X-Requested-With: XMLHttpRequest is always added, but default value can be changed

Values can also then be overridden in beforeSend function
Request Behaviour
method and type
GET
HTTP method to use for the request
  • Was called type before jQuery 1.9.0 
url
Current page
URL to send request to
async
true
If you need synchronous requests, set this option to false
  • Synchronous requests may temporarily lock the browser, disabling any actions while the request is active
  • Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation.
  • As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done().

cache
true

false for dataType 'script' and 'jsonp'
By default, requests are always issued, but the browser may serve results out of its cache

Set to false to force requested pages not to be cached by the browser
  • will only work correctly with HEAD and GET requests
  • works by appending "_={timestamp}" to the GET parameters
crossDomain
false for same-domain requests, true for cross-domain requests
If you wish to force a crossDomain request (such as JSONP) on the same domain
  • Allows, for example, server-side redirection to another domain
isLocal
Depends on protocol
Allow the current environment to be recognized as "local," (e.g. the filesystem)
  • protocols automaticaly recognized as local: file, *-extension, and widget.
xhr
Callback for creating the XMLHttpRequest object.
  • Defaults to the ActiveXObject when available (IE), the XMLHttpRequest otherwise.
  • Override to provide your own implementation for XMLHttpRequest or enhancements to the factory.
xhrFields
Object of fieldName-fieldValue pairs to set on the native XHR object.
  • Bug: In jQuery 1.5, the withCredentials property was not propagated to the native XHR and thus CORS requests requiring it would ignore this flag. 

$.ajax({
  url: a_cross_domain_url,
  xhrFields: {
    withCredentials: true
  }
});
Authentication
username

Username to be used with XMLHttpRequest in response to an HTTP access authentication request.
password

Password to be used with XMLHttpRequest in response to an HTTP access authentication request.
Script requests
script

Object with additional attributes to be used in a "script" or "jsonp" request.
  • If this object is provided it will force the use of a script-tag transport
  • Can be used to set nonce, integrity, or crossorigin attributes to satisfy Content Security Policy requirements.
scriptCharset

Sets the charset attribute on the script tag used in the request
  • Used when the character set on the local page is not the same as the one on the remote script
  • charset attribute can be specified in scriptAttrs instead
Request content (body or query parameters)
data

Data to be sent to the server
  • If the HTTP method is one that cannot have an entity body, such as GET, the data is appended to the URL.

POST data will always be transmitted to the server using UTF-8 charset, per the W3C XMLHTTPRequest standard.
  • Objects: jQuery generates the data string from the object's key/value pairs using jQuery.param() unless the processData option is set to false
  • Arrays: serializes multiple values with same key based on the value of the traditional setting
  • String: should already be encoded using the correct encoding for contentType

In requests with dataType: "json" or dataType: "jsonp", if the string contains a double question mark (??) anywhere in the URL or a single question mark (?) in the query string, it is replaced with a value generated by jQuery that is unique for each copy of the library on the page (e.g. jQuery21406515378922229067_1479880736745).
processData
true
By default, data passed in to the data option as anything other than a string will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded”
  • Set to false if you want to send a DOMDocument, or other non-processed data
traditional

true if you wish to use the traditional style of param serialization (see serializaiton section above)
Response behaviour
contents

Object of string or regular expression keys that determine how to parse response
converters

dataType-to-dataType converters:

Default:

{
  "* text": window.String, 
  "text html": true, 
  "text json": jQuery.parseJSON, 
  "text xml": jQuery.parseXML
}

Use transports when converters and prefilters aren’t flexible enough.
dataType
Intelligent Guess (xml, json, script, or html)
Type of data that you're expecting back from the server
  • If none is specified, jQuery will try to infer it based on the MIME type of the response
Data type
Preprocessing
Desscription
“text"
None

Passed to success as jqXHR .responseText (String)

A plain text string.
"html"
Returns HTML as plain text; included script tags are evaluated when inserted in the DOM.
"json"

jQuery.parseJSON

Passed to success as jqXHR .responseJSON (Object)
Evaluates the response as JSON and returns a JavaScript object.
  • Cross-domain "json" requests that have a callback placeholder, e.g. ?callback=?, are performed using JSONP unless the request includes jsonp: false in its request options.
  • The JSON data is parsed in a strict manner; any malformed JSON is rejected and a parse error is thrown.
  • As of jQuery 1.9, an empty response is also rejected; the server should return a response of null or {} instead. (See json.org for more information on proper JSON formatting.)
"script"
Execute the JavaScript that is received from the server

Passed to success as (String)
Evaluates the response as JavaScript and returns it as plain text. Disables caching by appending a query string parameter, _=[TIMESTAMP], to the URL unless the cache option is set to true.
  • Note: This will turn POSTs into GETs for remote-domain requests. Prior to jQuery 3.5.0, unsuccessful HTTP responses with a script Content-Type were still executed.
"jsonp"
Automatically append a query string parameter of (by default) callback=? to the URL

Server should return valid JavaScript that passes the JSON response into the callback function.

jQuery will execute the returned JavaScript, calling the JSONP callback function

Passes JSON object containing response to success
Loads in a JSON block using JSONP. Adds an extra "?callback=?" to the end of your URL to specify the callback.
  • Disables caching by appending a query string parameter, "_=[TIMESTAMP]", to the URL unless the cache option is set to true.

"xml"
jQuery.parseXML

Passed to success as jqXHR .responseXML (XMLDocument)
Returns XML document that can be processed via jQuery.
multiple, space-separated values

As of jQuery 1.5, jQuery can convert a dataType from what it received in the Content-Type header to what you require.
  • For example, if you want a text response to be treated as XML, use "text xml" for the dataType. You can also make a JSONP request, have it received as text, and interpreted by jQuery as XML: "jsonp text xml". Similarly, a shorthand string such as "jsonp xml" will first attempt to convert from jsonp to xml, and, failing that, convert from jsonp to text, and then from text to xml.
statusCode

Object of numeric HTTP codes and functions to be called when the response has the corresponding code
  • If the request is successful, the status code functions take the same parameters as the success callback
  • if it results in an error (including 3xx redirect), they take the same parameters as the error callback.

$.ajax({
  statusCode: {
    404: function() {
      alert( "page not found" );
    }
  }
});
dataFilter

Function to be used to handle the raw response data
  • pre-filtering function to sanitize the response.
  • Arguments:
    • The raw data returned from the server and
    • The 'dataType' parameter.
  • should return the sanitized data

Function( String data, String type ) => Anything
timeout

Timeout (in milliseconds) for the request
  • 0 means there will be no timeout
  • Overrides any global timeout set with $.ajaxSetup()
  • Starts at the point the $.ajax call is made (if several other requests are in progress and the browser has no connections available, it is possible for a request to time out before it can be sent)
  • n jQuery 1.4.x and below, the XMLHttpRequest object will be in an invalid state if the request times out; accessing any object members may throw an exception.
  • In Firefox 3.0+ only, script and JSONP requests cannot be cancelled by a timeout; the script will run even if it arrives after the timeout period.
ifModified
false
Successful only if the response has changed since the last request
  • Checks Last-Modified header
  • As of jQuery 1.4, also checks the 'etag' specified by the server to catch unmodified data.
Callbacks
context
Set the value of this for callbacks

$.ajax({
  url: "test.html",
  context: document.body
}).done(function() {
  $( this ).addClass( "done" );
});
global
Whether to trigger the global Ajax event handlers for this request (see below)
  • useful to, for example, suppress a loading indicator that was implemented with .ajaxSend() if the requests are frequent and brief.
  • With cross-domain script and JSONP requests, the global option is automatically set to false
jsonp and jsonpCallback
Override the callback function name in a JSONP request (string or boolean)
  • value will be used instead of 'callback' in the 'callback=?' part of the query string in the url.

{ jsonp:’onJSONPLoad’ } // result in 'onJSONPLoad=?' passed to the server

Specify the callback function name for a JSONP request
  • value will be used instead of the random name automatically generated by jQuery
  • preferable to let jQuery generate a unique name as it'll make it easier to manage the requests and provide callbacks and error handling
  • may want to specify the callback when you want to enable better browser caching of GET requests
  • As of jQuery 1.5, you can also use a function for this setting, in which case the value of jsonpCallback is set to the return value of that function

{ jsonpCallback:’foo’ }

beforeSend
Can be used to modify the jqXHR object before it is sent
  • Use this to set custom headers
  • Returning false will cancel the request
  • As of jQuery 1.5, the beforeSend option will be called regardless of the type of request

Function( jqXHR jqXHR, PlainObject settings )
success
Function to be called if the request succeeds
  • data returned from the server, formatted according to the dataType parameter or the dataFilter callback, if also specified
  • A string describing the status
  • jqXHR (in jQuery 1.4.x, XMLHttpRequest) object
  • Can accept an array of functions to be called in series, as of jQuery 1.5

Function( Anything data, String textStatus, jqXHR jqXHR )
error
Function to be called if the request fails
  • textStatus can be null, "timeout", "error", "abort", or "parsererror"
  • errorThrown receives the textual portion of the HTTP status
  • Can accept an array of functions to be called in series, as of jQuery 1.5

Function( jqXHR jqXHR, String textStatus, String errorThrown )

complete
When the request finishes (after success and error callbacks are executed)
  • string categorizing the status of the request ("success", "notmodified", "nocontent", "error", "timeout", "abort", or "parsererror")
  • As of jQuery 1.5, the complete setting can accept an array of functions. Each function will be called in turn

Function( jqXHR jqXHR, String textStatus )

Other utilities

If you are already using jQuery, and need to support older browsers, you can often save the overhead of a polyfill or the headache of detecting browser support by using the implementations that jQuery provides.

If you don’t need to support older browsers, need a polyfill for another reason, or are using some sort of JavaScript transpilation build pipeline, then going with the more native JavaScript functions may be a better idea.

Arrays

Use jQuery’s methods iterating and filtering arrays:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Iteration
$.each(arrayOrObject, callback);

// Transformation
$.map(arrayOrObject, callback);

// Filter
$.grep( [ 0, 1, 2 ], function( n, index ) {
  return n > 0;
});

// Concatenation
$.merge(array1, array2);

Objects

Similarly, jQuery provides helpers for objects:

1
2
3
4
5
6
7
// Query methods   
$.isEmptyObject({});
$.isPlainObject({});
$.type(target)

// Merging
$.extend({}, object1, object2);

Comments