Opinionated jQuery Style Guide for teams by De Voorhoede.
This guide aims to improve the way your team uses jQuery. It helps you write code which is
.ready()
jQuery
to $
.first()
for single element.on()
for event binding.show()
, .hide()
and .toggle()
.css()
.animate()
jQuery is a utility library for easy DOM access & manipulation, event handling, Ajax and more. By using jQuery you can write consise and expressive code which works across modern and legacy browsers. jQuery has extensive tests, detailed documentation, a large active community and an ecosystem of plugins.
Consider native browser featuresSince the release of jQuery many of its features now have a native browser equivalent. For example adding a class to an element can now be achieved via element.classList.add(className)
instead of $(element).addClass(className)
. Depending on the needs of your project you may be able to use only native browser features.
jQuery is the swiss army knife for DOM and event handling and much more. While jQuery offers a wide range of features, you might not need most of them in your project. Simply because you have little functionality or only modern browsers to support. In that case consider a lightweight alternative.
jQuery's .ready()
ensures your script is not executed before the DOM is ready. This is important because we typically want to access the DOM in our script. However, since the script can't be executed before the DOM is ready, a better practice is to defer script execution.
.ready()
means scripts have been loaded too early. Therefore defer loading instead.Avoid loading scripts too early and waiting for the DOM to be ready using .ready()
. Defer script loading by placing scripts just before the closing </body>
tag or using the defer
attribute:
<!-- recommended: load in tail --> <body> ... <script src="path/to/jquery.min.js"></script> <script>/* DOM is ready, use jQuery directly */</script> </body>
<!-- recommended: defer script loading --> <head> ... <script defer src="path/to/jquery.min.js"></script> <script defer src="path/to/my-app.min.js"></script> </head>
Note: Be aware using defer
in IE <= 9 can cause issues. So consider your browser scope before using this setup.
<!-- avoid: --> <head> ... <script src="path/to/jquery.min.js"></script> <script>jQuery.ready(function(){ /* ... */ });</script> <!-- same applies to external script containing `.ready()` --> </head>
jQuery
to $
is a common practice, which developers are familiair to.jQuery
to $
within a scope, avoids unexpected conflict with other scripts using $
.$
.Explicitly assign jQuery
to $
within a scope. When using a module loader (like CommonJS) assign it directly to a variable named $
. Otherwise use an IIFE (immediately-invoked function expression):
/* recommended when using module loader, like CommonJS: */ const $ = require('jquery'); // use jQuery as $
/* recommended: use an IIFE */ (function($){ // use jQuery as $ }(jQuery));
Every call to $(element}
asks jQuery to rescan for the matching element, wrap it in a jQuery object, and create a new instance of something you already have in memory. This is something avoidable if you already did it once.
/* avoid: repeating jQuery lookups */ $('button').addClass('is-active'); $('button').on('click', function(event) {}); /* recommended: cache jQuery lookup in variable */ var $button = $('button'); $button.addClass('is-active'); $button.on('click', function(event) {});Optimise selectors for performance
/* avoid: overly specific */ var $amount = $('[data-table] [data-table-amount]'); var $percentage = $('[data-table] [data-table-percentage]'); /* recommended: using `.find()` which is highly optimised on the parent element */ var $table = $('[data-table'); var $amount = $table.find('[data-table-amount]'); var $percentage = $table.find('[data-table-percentage]');Use
.first()
for single element
jQuery always returns a collection when using $(selector)
, while sometimes you are only interested in / only expect one element. In vanilla JS you would use .querySelector(selector)
instead of .querySelectorAll(selector)
.
To make it clear for other developers of you intention of just using one element
/* collection of buttons (akin querySelectorAll) */ $buttons = $form.find('button'); /* versus just a single button (akin querySelector) */ $submitButton = $form.find('[type="submit"]').first();
Naturally variable names should also reflect this. So plural for collections ($buttons
), singular for a individual element ($button
).
Methods like .click()
or .change()
are just alias for .on('click')
and .on('change')
.
.on()
supports event delegation resulting in more flexibility and better performance./* avoid: .click() */ $button.click(function(event) {}); /* recommended: .on() */ $button.on('click', function() {});
When a selector is provided, the event handler is referred to as delegated. jQuery bubbles the event from the event target up to the element where the handler is attached and runs the handler for any elements along that path matching the selector.
— jQuery
$list = $('[todo-list]').first(); $items = $list.find('[todo-item]'); /* avoid: event listener on each item */ $items.on('click', function(event) { /* ... */ }); /* recommended: event delegation on list */ $list.on('click', '[todo-item]', function(event) { /* ... */ });Avoid
.show()
, .hide()
and .toggle()
JQuery lets you .show()
, .hide()
and .toggle()
elements. jQuery toggles an inline display: none
to achieve this. HTML5 introduces a new global attribute named [hidden]
, which is styled as display: none
by default. It's better practice to use this native standard and toggle [hidden]
instead of using .show()
, .hide()
and .toggle()
.
.show()
, .hide()
and .toggle()
as they use inline styles, which are hard to overwrite.[hidden]
as it semantically indicates an element is not yet, or no longer, relevant.Add, remove or toggle the [hidden]
attribute instead of using .show()
, .hide()
or .toggle()
.
Note: If you need to support pre HTML5 browsers use CSS to style [hidden]
correctly:
/* recommended: ensure hidden elements are not displayed in pre HTML5 browsers */ [hidden] { display: none !important; }
/* avoid: `.show()` elements */ $elements.show(); /* recommended: add `[hidden]` attribute */ $elements.attr('hidden', '');
/* avoid: `.hide()` elements */ $elements.hide(); /* recommended: remove `[hidden]` attribute */ $elements.removeAttr('hidden');
/* avoid: `toggle()` elements */ $elements.toggle();
/* recommended: toggle hidden attribute (with jQuery): */ // add `toggleHidden` functionality as jQuery plugin $.fn.toggleHidden = function() { return this.each(function(index, element) { var $element = $(element); if ($element.attr('hidden')) { $element.removeAttr('hidden') } else { $element.attr('hidden', ''); } }); }; // call `toggleHidden` on element: $elements.toggleHidden();
/* recommended: toggle hidden attribute (without jQuery): */ $elements.get().forEach(toggleHidden); function toggleHidden(element) { if (element.hasAttribute('hidden')) { element.removeAttribute('hidden') } else { element.setAttribute('hidden', ''); } }
jQuery can get and set styling directly on an element with the .css()
method. When using .css()
to set CSS it will set the styles inline and you will be mixing concerns (styling and logic).
.css()
method is not used it can be omitted when creating a custom jQuery build. This reduces file size./* avoid: mixing JavaScript and CSS */ $element.css('border', '1px solid green');
/* recommended: using a class */ $element.addClass('is-active');
/* CSS */ .is-active { border: 1px solid green; }Prefer CSS animations over
.animate()
jQuery lets you create complex animation sequences using .animate(). Since the introduction of jQuery native CSS has caught up and now also provides methods to transition and animate elements.
For simple animations use a CSS transition:
/* avoid: jquery animate */ $element.animate({ left: '50px' }, 150, 'easeout');
/* recommended: css animations */ $element.addClass('is-active');
/* vendor prefix might be required */ .is-active { transform: translate(50px); transition: transform 150ms ease-out; }
For more complex animations use a CSS keyframes animation:
/* avoid: jquery animate */ function blink() { $element .animate({ opacity: 0 }, 1000) .animate({ opacity: 1 }, 1000, blink); } blink();
/* recommended: css animations */ $element.addClass('is-blinking');
/* vendor prefix might be required */ .is-blinking { animation: blink 2s infinite; } @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } }Prefer native Array methods
jQuery’s array methods are non-standard. They use the signature (index, item/element)
while native uses (item/element, index)
.
Make sure you check your browser scope supports native array methods. Then use .get()
to get a native array of HTML elements. Use native array methods, like forEach
, map
, filter
and reduce
to process the elements:
/* avoid: jQuery array methods */ $elements.map((index, el) => /* ... */) /* recommended: use native methods */ $elements.get().map(el => /* ... */)Prefer promises over callbacks
A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers to an asynchronous action's eventual success value or failure reason.
Use promises instead of callbacks to keep code more readable and future proof when handling asynchronous requests. Promises can also be passed around so other modules can chain (.then) onto it, instead of complex callbacks inside callbacks (pyramid of doom) structures.
/* avoid: callbacks */ $.ajax('example.com/articles/1/author', { success: function(response) {}, error: function(err) {} }); /* recommended: use promise */ var request = $.ajax('example.com/articles/1/author'); request.then(function(response) {}); request.catch(function(err) {});
Linters like ESLint and JSHint improve code consistency and help trace syntax errors.
Configure your linter to accept jQuery and $
as global variable.
{ "env": { "browser": true }, "globals": { "jQuery": true, "$": true } }
{ "jquery": true, "browser": true }
Special builds can be created that exclude subsets of jQuery functionality. This allows for smaller custom builds when the builder is certain that those parts of jQuery are not being used.
— jQuery
$('p').css('red')
, etc).Follow the official documention on creating a custom build to get you setup.
Then create your own custom build excluding modules you don't need:
grunt custom:-css,-css/showHide,-deprecated,-effects,-event/alias,-core/ready,-exports/amd
This custom build is an example that reflects the guidelines presented. Following this guide saves you 17KB (6kb gzipped) on your final jQuery size.
De Voorhoede waives all rights to this work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4