Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, so there are things we should avoid that reduce the quality of our code. By following best practices, we can create elegant and manageable code that’s easy for anyone to work with.
In this article, we’ll look at ways to reduce DOM manipulation, use shortcut notation, and having functions perform a single task.
Avoid DOM Manipulation
In our JavaScript, we should avoid manipulating the DOM if we want to display something dynamically. We can easily put a lot of styling code in our CSS instead of manipulating it directly with JavaScript.
For example, if we want to make an input display a red border when the input is invalid on submit, we can write the following HTML code:
<form>
<input type='text' required>
<button type='submit'>
Submit
</button>
</form>
Along with the following JavaScript code:
const input = document.querySelector('input');
const form = document.querySelector('form');
form.onsubmit = (e) => {
e.preventDefault();
}
input.oninvalid = () => {
input.style.borderColor = 'red';
input.style.borderStyle = 'solid';
input.style.borderWidth = '1px';
}
As we can see, in the event handler we assigned to the oninvalid
property of the input, we set the border with JavaScript.
However, we can instead do it with CSS by creating a class and setting the class name in JavaScript instead.
We keep the same HTML code, but add the following CSS code:
.invalid {
border: 1px solid red;
}
Then in our JavaScript code, we replace what we had with:
const input = document.querySelector('input');
const form = document.querySelector('form');
form.onsubmit = (e) => {
e.preventDefault();
}
input.oninvalid = () => {
input.className = 'invalid';
}
As we can see, it does the same thing in a much shorter way. It requires less processing power since it isn’t setting the styles dynamically.
Also, now we have a class and style that we can reuse in other HTML elements so that we don’t have to repeat code.
Using Shortcuts
In JavaScript, there are lots of shortcuts that are still clear for developers to read.
For example, when we create an object, we can either use the Object
constructor or the object literal notation.
With the Object
constructor, we can create an object as follows:
let obj = new Object();
obj.foo = '1';
obj.bar = '2';
obj.baz = function() {
console.log('baz');
}
Alternatively, we can write the same thing in the object literal notation:
let obj = {
foo: '1',
bar: '2',
baz() {
console.log('baz');
}
}
We have the same number of lines, but we have to repeat the object name in every line in the first example. Whereas we don’t have to do that with the object literal.
Object literals also improve clarity, so we can use that instead of the Object
constructor to create an object. They both do the same thing, but we don’t have to repeat code as we did by creating an object with the Object
constructor.
Likewise, instead of defining an array as follows:
let arr = new Array();
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
We can define it in a much shorter way by writing:
let arr = [1, 2, 3, 4, 5];
As we can see, it’s way shorter to define it with the array literal than the Array
constructor. Also the Array
constructor is confusing since it has 2 signatures. If we pass in one argument, then we get the array with the length set by the argument. Otherwise, we get an array with the arguments we passed in as the content.
Another handy shortcut is the ternary operator, where we can assign things to a variable conditionally.
For example, instead of writing:
const x = 100;
let foo;
if (x === 100) {
foo = 'bar';
} else {
foo = 'baz';
}
We can write:
const x = 100;
let foo = (x === 100) ? 'bar' : 'baz';
In the code above:
let foo;
if (x === 100) {
foo = 'bar';
} else {
foo = 'baz';
}
is the same as:
let foo = (x === 100) ? 'bar' : 'baz';
They both check if x
is 100 then, then assign 'bar'
to foo
if it is, and assign 'baz'
otherwise.
The code is much shorter with the ternary operator and we get the same result. It also doesn’t make the code harder to read.
Photo by Austin Distel on Unsplash
One Function Per Task
Generally, a function should only do one thing. This makes each function short and easy to read.
It also makes reusing it easier since there's less chance of conflicting functionality in each function.
Also, it’s a good idea to create helper functions for common tasks.
For example, if we want to create some elements on the fly on a page. Either we can write:
const createPage = () => {
const items = ['foo', 'bar', 'baz'];
const ul = document.createElement('ul');
let li;
for (const item of items) {
li = document.createElement('li');
li.textContent = item;
ul.appendChild(li);
}
document.body.appendChild(ul);
const googleLink = document.createElement('a');
googleLink.href = '[http://www.googole.com'](http://www.googole.com%27);
googleLink.textContent = 'Google';
document.body.appendChild(googleLink);
const yahooLink = document.createElement('a');
yahooLink.href = '[http://www.yahoo.com'](http://www.yahoo.com%27);
yahooLink.textContent = 'Yahoo';
document.body.appendChild(yahooLink);
}
createPage();
Or we can write:
const createLink = (textContent, href) => {
const link = document.createElement('a');
link.href = href;
link.textContent = textContent;
return link;
}
const createUl = (items) => {
const ul = document.createElement('ul');
let li;
for (const item of items) {
li = document.createElement('li');
li.textContent = item;
ul.appendChild(li);
}
return ul;
}
const items = ['foo', 'bar', 'baz'];
const ul = createUl(items);
const googleLink = createLink('Google', '[http://www.google.com'](http://www.google.com%27));
const yahooLink = createLink('Google', '[http://www.yahoo.com'](http://www.yahoo.com%27));
document.body.appendChild(ul);
document.body.appendChild(googleLink);
document.body.appendChild(yahooLink);
We should write it the second way because we divide our code into functions that always return the same thing given the same input.
Also, they can be called anywhere. So if we want another link we can call createLink
again for example.
We also return the element, so we can attach it outside the functions, which means we can create elements on the fly without attaching them right away.
Conclusion
When we deal with client-side JavaScript code, we should avoid DOM manipulation as much as possible for increased performance and less code complexity.
Also, lots of JavaScript shortcuts make sense, like using literals whenever they’re available instead of the constructor.
Finally, we should divide code into functions that each does a single task to keep things easy to read and maintain. It also makes functions easier to reuse since they don’t have conflicting functionality.