Home

Awesome

jQuery Style Guide

Opinionated jQuery Style Guide for teams by De Voorhoede.

style guide jquery

Purpose

This guide aims to improve the way your team uses jQuery. It helps you write code which is

Table of Contents

About jQuery

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 features

Since 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.

Why?

How?

↑ back to Table of Contents

Consider lightweight alternative

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.

Why?

How?

↑ back to Table of Contents

Avoid .ready()

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.

Why?

How?

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>

↑ back to Table of Contents

Assign jQuery to $

Why?

How?

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));

↑ back to Table of Contents

Cache jQuery lookups

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.

Why?

/* 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) {});

↑ back to Table of Contents

Optimise selectors for performance

Why?

How?

/* 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]');

↑ back to Table of Contents

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).

Why?

To make it clear for other developers of you intention of just using one element

How?

/* 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).

↑ back to Table of Contents

Use .on() for event binding

Methods like .click() or .change() are just alias for .on('click') and .on('change').

Why?

How?

/* avoid: .click() */
$button.click(function(event) {});

/* recommended: .on() */
$button.on('click', function() {});

↑ back to Table of Contents

Use event delegation

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

Why?

How?

$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) { /* ... */ });

↑ back to Table of Contents

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().

Why?

How?

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; }

Show elements

/* avoid: `.show()` elements */
$elements.show();

/* recommended: add `[hidden]` attribute */
$elements.attr('hidden', '');

Hide elements

/* avoid: `.hide()` elements */
$elements.hide();

/* recommended: remove `[hidden]` attribute */
$elements.removeAttr('hidden');

Toggle elements

/* 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', '');
	}
}

↑ back to Table of Contents

Avoid using .css()

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).

Why?

How?

/* 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; }

↑ back to Table of Contents

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.

Why?

How?

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; }
}

↑ back to Table of Contents

Prefer native Array methods

Why?

jQuery’s array methods are non-standard. They use the signature (index, item/element) while native uses (item/element, index).

How?

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 => /* ... */)

↑ back to Table of Contents

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.

Why?

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.

How?

/* 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) {});

↑ back to Table of Contents

Lint your script files

Linters like ESLint and JSHint improve code consistency and help trace syntax errors.

Why?

How?

Configure your linter to accept jQuery and $ as global variable.

ESLint

{
	"env": {
		"browser": true
	},
	"globals": {
		"jQuery": true,
		"$": true
	}
}

JSHint

{
	"jquery": true,
	"browser": true
}

↑ back to Table of Contents

Create a smaller build

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

Why?

How?

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.

↑ back to Table of Contents


License

CC0

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.