jQuery is a JavaScript library that provides necessary APIs to easily deal with cross browsing issues in a fragmented browser environment. It was first released in 2006 by John Resig, and since then, it has been the most popular JavaScript library on the web. However, since developers can work with DOM and Ajax requests with the standard APIs now, and because the gap among different browsers is closing, jQuery is slowly fading into history. Also, with the recent rise of single-page applications (SPA), most of jQuery’s roles have been replaced with frameworks like React and Vue. Meanwhile, if SPAs are unnecessary or if building with the intention of support older browsers, jQuery is still extremely useful, and allows developers to write concise and effective cross-browser codes.

This guide is written to help developers use jQuery more efficiently and use it to write manageable codes. To read more about detailed explanations of jQuery API, refer to the official website.

Table of Contents

Choosing the Right Version

All versions of jQuery (from 1.X to 3.X) support various browsers including Firefox, Chrome, Safari, Opera, and etc. By choosing the right version of jQuery, developers can satisfy the browser support scope of the service.

  • v1.X : Supports IE6 and above
  • v2.X, 3.X : Does not support IE 6~8, but supports IE9 and above

It is recommended against using jQuery 1.7 or below due to its outdatedness and the inevitable fact that it may conflict with today’s modern browsers. Since modern builds are more stable compared to older versions, it is safer to just use the latest and stable build.

  • From the perspective of this guide (2018 September,) v3.X is the most current, and it supports IE9 and above.


jQuery can be installed using npm, bower, CDN or by downloading straight from the repository, and the method of install should be decided with the project characteristics in mind.

Using npm to Install

To use npm to download the source code, use the following command line prompts.

$ npm install --save jquery # Latest version
$ npm install --save jquery@<version> # Specific version

Using bower to Install

To use bower to download the source code, use the following command line prompts.

$ bower install jquery # Latest Version
$ bower install jquery#<tag> # Specific Version

Using CDN

As in the example below, it is possible to simply use the CDN of the desired version of jQuery. Possible CDNs for jQuery include the links from the official jQuery website, Google, and Microsoft.

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

However, keep in mind that using a CDN will affect the product if there is a change to the URL or if there are network errors on the source side, so this document advises against using CDN.

Note [FE Guide] Anti-Pattern - Do not use the direct URL when using external resources.

Downloading Directly

Necessary version of jQuery can be downloaded directly from the official website.

$ Identifier

jQuery is composed of a single function named jQuery, and this function provides everything from the namespace to all of its features. Loading jQuery to the global scope uses the $ identifier to refer to jQuery, so $ is just used for the sake of simplicity. However, when using libraries like prototype.js that also uses the $ identifier, jQuery’s $ identifier may override the previous identifier. In such cases, calling the $.noConflict() method can be used to restore the value of the original $ identifier, so that such libraries can be used at the same time.

<script src="other_lib.js"></script>
<script src="jquery.js"></script>
jQuery( document ).ready(function( $ ) {
  // only jQuery's $ identifier allowed
// Can use different library's $ identifier.

DOM Traversal

Preface jQuery Object Variables with $

jQuery object variables should be prefaced with the $ in order to easily differentiate native DOM elements with jQuery objects.

// good
const $myId = $('#myId');

// Bad
const myId = $('#myId');

If possible, Use ID Selectors

Since jQuery uses document.getElementById() to find elements, using id selectors provides a performance boost.

// Good

// Bad

Do Not Use ID Selectors with Other Selectors

Ids are unique in the document, so using ids with other selectors defeats the purpose.

// Good

// Bad
$('#outer #inner');

Do Not Use Class Selectors with Tag Selectors

// Good
const $products = $('.products');

// Bad
const $products = $('div.products');

Use Selectors in Logical Order

Using selectors in logical order, “tag” or “class” names in the left and “tag.class” at the end, allows jQuery to find elements faster. For example, when searching for “li.item”, jQuery first finds every element that is “li” and then searches for the element with “item” class. Then, finally, jQuery uses the selectors to traverse the DOM tree.

// Good
$('.wrap li.item');

// Bad
$('ul.wrap .item');

Do Not Use Complex Selectors

Keep the selector length to a minimum, and make sure never to use more than two selectors at once. Complex selectors slow down the performance of the entire program and makes it harder to read.

<div id="container" class="container-class first">
  <table id="table-id">
      <th class="first">Firstname</th>
      <td  class="first">Jill</td>
      <td class="first">Eve</td>

Below is an example of selecting all elements with the first class selectors from the HTML code above.

// Good
$('#table-id .first');

// Bad
$('#container table#table-id tr th.first, td.first')

Provide Context Information to the Selector to Limit the Search Field

// Good: Gets faster results because the program knows to look for an element with id of class-container

// Bad: Checks every element that has an 'item' class, so slower in comparision

Use .find() to Search for a Child Element of a Certain ID

Since it does not have to go through the Sizzle, .find() produces faster results.

// Good - #products tag has already been found internally through document.getElementById(), and only div.container needs to go through the Sizzle selector engine, so is faster.
const $productIds = $('#products').find('div.container');

// Bad - All redundant queries go through Sizzle selector engine.
const $productIds = $('#products div.container');

Avoid Global Selectors

// Good
$('.category input:radio'); 

// Bad
$('.buttons > *');
$('.category *:radio'); 

When Using Virtual Selectors, Do Not Omit Tag Selectors

If a virtual selector is used without the scope of a tag selector, jQuery engine searches through the entire document, so the program runs slower.

// Good
$('div.someclass input:radio');

// Bad
$('div.someclass :radio');

DOM Manipulation

When Manipulating Already Existing Elements, Always Detach()

detach() method allows developers to manipulate the element only on memory, detached from the actual DOM, so it prevents unnecessary layout operations.

<div id="table_container">

// Good
<script type="text/javascript">
  const parent = $( "#table_container" );
  const table = parent.find('table');
  table.append('<tr><td>1</td><td>John Smith</td></tr>');

// Bad
<script type="text/javascript">
  $('#table_container table').append('<tr><td>1</td><td>Hanjung</td></tr>');

Always Make Sure That the Element Actually Exists

// Good
const $mySelection = $('#nosuchthing');
if ($mySelection.length) {

// Bad: Only realizing that the element does not exist after several runs of the procedure.

Avoid Inline CSS Styling

CSS and JavaScript have different roles in web development; therefore, it is wiser, in terms of maintenance, to manage them separately. Define classes in CSS, and use JavaScript to manipulate the style by adding or deleting the classes.

/* Good */
.error { color: red; font-weight: bold; }
// Good

// Bad
$("#mydiv").css({'color':red, 'font-weight':'bold'});

Event Handling

Do Not Define Event Handlers Directly onto HTML

Defining event handlers within the HTML makes it difficult to actively configure or delete events. Also, it complicates the debugging process because having JavaScript on two different files means having to check two different files.

// Good
$('#myLink').on('click', myEventHandler); 
<!-- Bad -->
<a id="myLink" href="#" onclick="myEventHandler();">my link</a>

Use Event Delegation

Delegating the parent event handler allows jQuery to handle all of the descendants, dynamically added or edited, at the same time.

$("#parent-container").on("click", "a", delegatedClickHandler);

Do Not Use Anonymous Functions in Event Handlers

Using anonymous functions in event handlers complicate debugging, maintenance, and testing.

// Good
function myLinkClickHandler(){...}
function myInitHandler(){...}
$('#myLink').on('click', myLinkClickHandler);

// Same as $(document).ready(myInitHandler). This method of writting is recommended since the ready statement was deprecated in 3.0

// Bad
$('#myLink').on('click', function(){...}); 
$(function(){ ... });

Use Only One ready( handler ) Per Page

Using only one ready() method per page makes debugging easier, and can be used to track the flow of the program.

For Custom Events, Specify the Namespace When Using off() Method

(Since unbind() method has been deprecated since 3.X, use off() method, instead.)

const validate = function() {
  // Validation Code
$( "form" ).on( "click.validator", "button", validate );
$( "form" ).on( "keypress.validator", "input[type='text']", validate );

// 'offs' all of event handlers under the 'validator' namespace.
$( "form" ).off( ".validator" );

Method Chaining

Use Method Chaining

Method chaining offers many benefits including cached variable and time saved by not having to make redundant calls to selectors.

// Good

// Bad

Use Line Change when Using More Than Three Method Chains

// Good
  .on('click', myClickHandler)
  .on('mouseover', myMouseOverHandler)
// Bad
$('#myLink').addClass('bold').on('click', myClickHandler).on('mouseover', myMouseOverHandler).show();

Use Object Literal Instead of Method Chaining When Repeatedly Changing Attributes

// Good
  href: '#',
  title: 'my link',
  rel: 'external'

// Bad
$myLink.attr('href', '#').attr('title', 'my link').attr('rel', 'external');


Do Not Specify the Protocol in Request URL

Use Protocol-relative URL to enable the browser to judge the protocol on its own.

// Good
  url: '//www.nhn.com/api/hello',

// Bad
  url: 'https://www.nhn.com/api/hello',

When Using GET Requests, Instead of Including Parameters in the URL, Use data Object

Doing so will increase readability and make debugging easier.

// Good
  url: 'something.php',
  data: { 
    param1: test1, 
    param2: test2 

// Bad
  url: 'something.php?param1=test1&param2=test2',

Specify The dataType to Avoid Confusion

const jqxhr = $.ajax({
  url: url,
  dataType: 'json',

Using Promise Interface Allows For More Intuitive Understanding of the Code

$.ajax({ ... }).then(successHandler, failureHandler);

// The code above can also be written as shown below.
const jqxhr = $.ajax({ ... });

jQuery 3.0 and ES2015

This section briefly explains the new ES2015 compatibility features introduced in jQuery 3.0.

jQuery Collection Can Be Used in for…of Statements

The for…of statement, introduced in ES2015, supports all objects that support Iterable Protocol. Starting from jQuery 3.0, jQuery collection also supports iterable protocols, and can be used with for…of statements.

const $elems = $(".someclass");
// jQuery Prior to jQuery 3.0
$elems.each((i, elem) => {
  // Used elem or "this"
// ES6
for (let elem of $elems) {
  // Used elem

jQuery.Deferred Is Compatible With Promise/A+

Previously, jqXHR object returned as a result of jQuery.ajax() was a Deferred object of jQuery. However, starting from jQuery 3.0, success, error, and complete methods provided by Deferred objects have been deleted. Instead, done, fail, and always of Deferred object were used, and then and catch methods in Promises/A+ became available.

const defer = $.Deferred();
const filtered = defer.then(null, (value) => {
  return value * 3;

filtered.done(( value ) => {
  alert( "Value is 3*6 =" + value );

All Objects With then() Methods Can Be Used As Inputs of jQuery.when()

This means that external promise libraries like Bluebird, not just native promise objects, are also supported. Also, if there are many inputs, it functions like Promise.all(), and if there is one or no input, functions like Promise.resolve().

const promise1 = new Promise((resolve, reject) => {

$.when(promise1).then((value) => { 
  console.log(value); // "foo"

jQuery.ready Can Be Used As A Promise

jQuery.ready can be converted into a promise by using jQuery.when or Promise.resolve().

Note Reference

$.when($.ready, $.getScript("optional.js")).then(() => {
 // When document is ready and optional.js runs 
}).catch(() => {
  // When error occurs


This guide has explored some things to keep in mind when using jQuery and new ES6 features supported in version 3.0. While jQuery is effective in the right hands, in the wrong hands, can severely damage the overall performance of the code or reduce readability of the code. Author hopes that this document can guide users to fully benefit from jQuery.

This document is an official Web Front-End development guide written and maintained by NHN Cloud FE Development Lab. Any errors, questions, and points of improvement pertaining to this document should be addressed to the official support channel (dl_javascript@nhn.com).

Last Modified
2019. 03. 29
FE Development LabBack to list