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.
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.
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.
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.
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
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
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.
Necessary version of jQuery can be downloaded directly from the official website.
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>
<script>
$.noConflict();
jQuery( document ).ready(function( $ ) {
// only jQuery's $ identifier allowed
});
// Can use different library's $ identifier.
</script>
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');
Since jQuery uses document.getElementById()
to find elements, using id selectors provides a performance boost.
// Good
document.getElementById("myId");
$("#myId");
// Bad
$('.myClass');
Ids are unique in the document, so using ids with other selectors defeats the purpose.
// Good
$('#inner');
// Bad
$('#outer #inner');
// Good
const $products = $('.products');
// Bad
const $products = $('div.products');
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');
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">
<tr>
<th class="first">Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td class="first">Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td class="first">Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
</table>
</div>
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')
// Good: Gets faster results because the program knows to look for an element with id of class-container
$('.class','#class-container');
// Bad: Checks every element that has an 'item' class, so slower in comparision
$('.item');
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');
// Good
$('.buttons').children();
$('.category input:radio');
// Bad
$('.buttons > *');
$('.category *:radio');
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');
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">
<table>
<tr>
<td>ID</td>
<td>Name</td>
</tr>
</table>
</div>
// Good
<script type="text/javascript">
const parent = $( "#table_container" );
const table = parent.find('table');
table.detach();
table.append('<tr><td>1</td><td>John Smith</td></tr>');
parent.append(table);
</script>
// Bad
<script type="text/javascript">
$('#table_container table').append('<tr><td>1</td><td>Hanjung</td></tr>');
</script>
// Good
const $mySelection = $('#nosuchthing');
if ($mySelection.length) {
$mySelection.slideUp();
...
}
// Bad: Only realizing that the element does not exist after several runs of the procedure.
$('#nosuchthing').slideUp();
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
$("#mydiv").addClass("error");
// Bad
$("#mydiv").css({'color':red, 'font-weight':'bold'});
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>
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);
Using anonymous functions in event handlers complicate debugging, maintenance, and testing.
// Good
function myLinkClickHandler(){...}
function myInitHandler(){...}
$('#myLink').on('click', myLinkClickHandler);
$(document).ready(myInitHandler);
$(myInitHandler);
// 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(){ ... });
Using only one ready() method per page makes debugging easier, and can be used to track the flow of the program.
(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 offers many benefits including cached variable and time saved by not having to make redundant calls to selectors.
// Good
$('#myDiv').addClass('error').show();
// Bad
$('#myDiv').addClass('error');
$('#myDiv').show();
// Good
$('#myLink')
.addClass('bold')
.on('click', myClickHandler)
.on('mouseover', myMouseOverHandler)
.show();
// Bad
$('#myLink').addClass('bold').on('click', myClickHandler).on('mouseover', myMouseOverHandler).show();
// Good
$myLink.attr({
href: '#',
title: 'my link',
rel: 'external'
});
// Bad
$myLink.attr('href', '#').attr('title', 'my link').attr('rel', 'external');
Use Protocol-relative URL to enable the browser to judge the protocol on its own.
// Good
$.ajax({
url: '//www.nhn.com/api/hello',
...
});
// Bad
$.ajax({
url: 'https://www.nhn.com/api/hello',
...
});
Doing so will increase readability and make debugging easier.
// Good
$.ajax({
url: 'something.php',
data: {
param1: test1,
param2: test2
}
});
// Bad
$.ajax({
url: 'something.php?param1=test1¶m2=test2',
....
});
const jqxhr = $.ajax({
url: url,
...
dataType: 'json',
...
});
$.ajax({ ... }).then(successHandler, failureHandler);
// The code above can also be written as shown below.
const jqxhr = $.ajax({ ... });
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);
This section briefly explains the new ES2015 compatibility features introduced in jQuery 3.0.
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
}
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;
});
defer.resolve(6);
filtered.done(( value ) => {
alert( "Value is 3*6 =" + value );
});
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) => {
resolve('foo');
});
$.when(promise1).then((value) => {
console.log(value); // "foo"
});
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 |