JavaScript

It is a common myth that JavaScript and accessibility don't go well together. This bad reputation is largely due to the very obtrusive manner in which JavaScript is generally applied, and not the fault of the language. JavaScript is a toolβ€” a means to achieve a goal. If you use a knife as a tool, you could slice bread with it or cause bodily harm. It is not the knife that determines how it is used.

https://developer.mozilla.org/en-US/docs/Learn/Accessibility/CSS_and_JavaScript

You should keep unobtrusive JavaScript in mind when creating your content. The idea of unobtrusive JavaScript is that it should be used wherever possible to enhance functionality, not built in it's entirety.

We use Javascript to enhance the users experience. Our thinking is that a site should be functional without it. Use graceful degredation.

Coding standards

We prefer using Object Orientated Programming (OOP).

At 93digital we base our coding standards on the Airbnb Coding rules with some exceptions:

"rules": {
  "semi": 2,
  "no-param-reassign": 0,
  "arrow-parens": ["error", "as-needed"],
  "no-plusplus": "off",
  "import/no-unresolved": "off",
  "import/extensions": "off",
  "prefer-arrow-callback": "off",
  "func-names": "off",
  "no-use-before-define": "off",
  "comma-dangle": [
    "error",
    {
      "functions": "never",
      "arrays": "only-multiline",
      "objects": "only-multiline"
    }
  ]
},
"env": {
  "browser": true,
  "es6": true
},
"extends": ["eslint-config-airbnb"],
"globals": {
  "jQuery": true,
  "google": true,
  "luna": true
}
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

Make sure eslint is set up in your text editor to ensure the above consistency. check the eslint documentation for more information.

Naming convention

  • Each file should solve only one specific purpose (ie: slider, accordion, or module specific).
  • Each filename should describe the core functionality of the feature.
  • Name of files should be lowercased and hyphenated.
  • Variable and Function names should use camelCase.

Closures

Always wrap your JavaScript in closures. This can prevent collisions with outher scripts. When a script is not wrapped in a closure, the current this is the window.

Avoid:

const element = document.querySelector(.element);

element.addEventListener('click', event => {
  console.log(event);
});
1
2
3
4
5

> Prefer:

// Vanilla JavaScript
(() => {
  const element = document.querySelector(.element);

  element.addEventListener('click', event => {
    console.log(event);
  });
})();

// JQuery
($ => {
  const $element = $(.element);

  $element.on('click', event => {
    console.log(event);
  });
})(jQuery);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Variables

Variables used for vanilla JavaScript elements must start with a letter, are camelCase, make sure naming conventions make sense and never abreviate.

Naming Convention

Avoid:

const $button = document.querySelector('button'); // JavaScript
const button = $('button'); // jQuery
1
2

> Prefer:

const button = document.querySelector('button'); // JavaScript
const $button = $('button'); // jQuery
1
2

Don't hoist your variables at the top of the page. Keep them within the proximity of where they are utilised.

Avoid:

const element = document.querySelector('.element-class');

...
...(40+ lines of code)
...
function displayElement(element) {
  element.style.display = 'block';
}

function hideElement(element) {
  element.style.display = 'none';
}
1
2
3
4
5
6
7
8
9
10
11
12

> Prefer:

...
...(40+ lines of code)
...
const element = document.querySelector('.element-class');

function displayElement(element) {
  element.style.display = 'block';
}

function hideElement(element) {
  element.style.display = 'none';
}
1
2
3
4
5
6
7
8
9
10
11
12

Functions and Hoisting

As stated in our introduction the primary purposes of our best practices is to have consistent coding standards across JS, CSS and PHP

For this reason, we take advantage of hoisting as PHP has a similar behaviour for non-conditional function.

Why?

It might be common in other languages to have the main block (or main function) at the bottom of the file, this has a downside. It forces you to jump to the end of the file to figure out how the functions are used.

Avoid:

// functions
function toggleElement(event) {
  const target = document.querySelector(event.dataset.target);

  target.style.display = target.style.display === 'block' ? 'none' : 'block';
}

function openPopup(event) {
  const newWindow = open('/', 'example', 'width=300,height=300')
  newWindow.focus();
}

// main block
const toggleElement = document.querySelector('.toggle-me');
toggleElement.addEventListener('click', event => toggleElement);

const openElements = document.querySelector('.open-me');
openElements.addEventListener('click', event => openPopup);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

In this example, the use of the two functions isn't explicit until we reach line 13, and although it might be OK for this short example, in a production environment the main block might not be visible in the viewport.

> Prefer:

// main block
document.querySelectorAll('.toggle-me', toggleElement);
document.querySelectorAll('.open-me', openPopup);

// functions
function toggleElement(e) {
  const target = document.querySelector(e.dataset.target);

  target.style.display = target.style.display === 'block' ? 'none' : 'block';
}

function openPopup(e) {
  const newWindow = open('/', 'example', 'width=300,height=300')
  newWindow.focus();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

In this example, within the first two lines the context of the functions are already clear.

Functions vs Arrow Functions

Although arrow functions are easier and quicker to write, that doesn't mean it's more readable.

We primarily use arrow functions:

  • when we need lexical this
  • looping items

for everything else we use functions.

Using arrow functions

Multi-line

const ourFunction = event => {
  console.log(event);
};
1
2
3

Single line

const ourFunction = event => console.log(event);
1

Lexical this

An arrow function expression is a syntactically compact alternative to a regular function expression, although without its bindings to the this, arguments, super, or new.target keywords.

MDN.

Concatenating strings

Before ES6 we would be required to concatenate using the + operator. When adding more that 2 strings this would become messy. Using the new template literals technique produces much clearer declerations.

Avoid:

const firstName = 'John';
const lastName = 'Smith';
const welcomeMessage = 'Welcome ' + firstName + ' ' + lastName + '!';
1
2
3

> Prefer:

const firstName = 'John';
const lastName = 'Smith';
const welcomeMessage = `Welcome ${firstName} ${lastName}!`;
1
2
3

Libraries

All plugins should be added via NPM or Yarn:

npm install --save [package-name]
yarn add [package-name] --dev
1
2

NOTE: don't forget to add –-save option when installing 3rd part scripts, otherwise they will not be included in package.json and won't be available for other developers.