AppPresser

How to Write Better Javascript

Most web projects involve writing Javascript, these days it’s more popular than ever.

It’s important to know the fundamentals of how to write good Javascript, even if you don’t become a JS Ninja. There are some simple guidelines you can follow to instantly upgrade your skills, and ensure clean, maintainable code.

Before we dive into that, let’s look at some beginner mistakes.

What Not To Do

I’ve seen some horrible Javascript, I mean the type of thing you can’t un-see.

I once saw a file with dozens of jQuery ready functions, one after another:

jQuery(document).ready(function($) {
	$('.button').click(function() {
		alert('do something');
	});
});

jQuery(document).ready(function($) {
	$('.another-button').click(function() {
		alert('do something else');
	});
});

jQuery(document).ready(function($) {
	$('#another-button2').click(function() {
		alert('do something else');
	});
});

jQuery(document).ready(function($) {
	$('#another-button3').click(function() {
		alert('do something else');
	});
});

...ad infinitum

This is way too much repeated code, and will quickly become difficult to read and maintain. You should only have the document ready function in one place, to initialize your code. We’ll talk more about that in a bit.

(Also, the .ready() function has been deprecated, so you shouldn’t use it anymore)

Another thing I’ve seen is the dreaded nested if statements:

if( $variable = condition ) {
	// do something

	if( $variable2 = condition ) {
		// do something

		if( $variable3 = condition ) {
			// do something

			if( $variable4 = condition ) {
				// do something

				if( $variable5 = condition ) {
					// do something
					
				}

			}

		}

	}

}

It’s really hard to tell what the conditions are for executing code inside the nested if statements, and it can quickly become overcomplicated. If your code looks like this, you may need to think through how you can simplify.

Another thing we don’t want is jQuery soup. This is where we have a bunch of jQuery functions with no particular organization.

$(function() {

	$('.click').click(function(){
		// do something
	});

	$('.header').css({'color': 'red'});

	$('#coffee').hover( makeMeCoffee );

});

This may work if you are only writing 3 functions, but projects always grow beyond that. No one wants to sort through a 600 line file of jQuery soup. We’ll look at better structure for your JS file later in this article.

There are a lot of bad practices out there, and we can’t cover them all, but here are some more things to avoid.

// Not using a closure means everything is a global, this is bad!
var isGlobal = true; // this will show up as window.isGlobal, polluting the global space

// Instead, use an anonymous function like below
(function() {

    // not using the var keyword makes a global
    myVar = 'bad';
    var myVar = 'good';

    // not caching variables
    document.getElementById('my-btn').style.color = 'red';
    document.getElementById('my-btn').innerHTML = 'Click me';

    // better
    var myBtn = document.getElementById('my-btn');
    myBtn.style.color = 'red';
    myBtn.innerHTML = 'Click me';

})();

Avoid globals and always cache your elements. Looking for an element every time you run a function uses a lot of memory, plus it’s just unnecessary.

Now that we have that out of the way, let’s look at some good examples.

Best Practices

We’ve looked at some of the things we shouldn’t do, so let’s look at best practices.

Here’s a boilerplate of how I like to start my script files:

(function(window, document, $, undefined){
	
	'use strict';

	window.myPrefix = {};

	myPrefix.init = function() {
		// get it started
	}

	myPrefix.init();

})(window, document, jQuery);

First of all, we are using a self executing anonymous function. It sounds really fancy, but it’s just an enclosure to make sure we aren’t polluting the global space. We saw above how defining variables outside of an enclosure makes them available in the window object, which can cause collisions and generally makes things messy.

You can put ‘use strict’ inside your closure to get better error messages when you do something you aren’t supposed to. For example:

"use strict";
x = 3.14;       // This will cause an error because we didn't use the var keyword

The next thing we do is create a global variable that will hold all of our variables and functions: window.myPrefix = {}. You should prefix this since it will be in the global space, but anything inside of it does not need a prefix. This is similar to using classes in PHP or ES6.

Everything will be contained inside of window.myPrefix, which means we are only adding one thing to the global namespace.

Next we kick things off with an init function: myPrefix.init(). Everything we do inside of our closure needs myPrefix.whatever to ensure it’s all going into our global variable.

Other developers can access myPrefix.init() from their Javascript if they want to manipulate our code. If we just use an anonymous function, other developers can’t access our code.

$(function() {

	function init() {
		// other developers can't access this
	}

	init();

});

Writing our code with an enclosure with a prefixed global variable allows us to keep it organized and accessible by other developers.

Here’s an example of caching some selectors, and adding another function.

(function(window, document, $, undefined){

	'use strict';

	window.myPrefix = {};

	myPrefix.init = function() {

		// developers can access this
		myPrefix.cacheSelectors();
		myPrefix.doSomething();

	}

	myPrefix.cacheSelectors = function() {

		// cache our selectors here, can use them throughout the file
		myPrefix.body = $('body');
		myPrefix.classes = 'class1 class2';

	}

	myPrefix.doSomething = function() {

		// example of using our cached selectors
		myPrefix.body.addClass( myPrefix.classes );

	}

	myPrefix.init();

})(window, document, jQuery);

Now we have a nice structure for our file, avoiding globals and caching selectors, and it’s accessible by other developers.

Since we are using jQuery, this file must be enqueued after the jQuery library. Using WordPress, we do this by adding jquery as a dependency for our script like this:

add_action( 'wp_enqueue_scripts', 'prefix_do_scripts' );

prefix_do_scripts() {

wp_enqueue_script( 'prefix-js', plugin_dir_url( __FILE__ ) . 'assets/js/prefix-script.js', array( 'jquery' ),'1.0', true );

}

Random Tips

Here are some common issues that we didn’t cover above.

Don’t do this:

$('.btn').click( function() {
// do something
});

$('.element').hover( function() {
// do something
});

$('#target').focus( function() {
// do something
});

Instead, you can chain your events by using .on() (formerly .bind). Also, cache and abstract your functions like below.

// cache all the things
var body = $('body');

var myHandler = function( e ) {
    e.preventDefault();
    // do something
}

body
// start chaining events
.on( 'click', '.btn', myHandler )

.on( 'hover', '.element', myHandler )

.on( 'focus', '#target', anotherHandler );

By chaining our events with .on() and abstracting our callbacks, we make this much easier to read. We can also add all of our events in one place for easier reference, and make sure everything is cached properly.

CSS in our JS

Anything related to CSS or animation should always be handled by adding and removing classes. For example, we don’t want to use:

$( '.btn' ).css( "color", "red" );

Instead, we can do:

$( '.btn' ).addClass( 'red' );

And in our style.css file we have:

.red { color: red }

The same applies for hiding and showing elements, and doing animations. You should never use jQuery slideToggle() or animate(), instead add and remove classes that use CSS animation.

If you follow these guidelines you’ll be well on your way to writing clean, maintainable Javascript. Shout out to Justin Sternberg who taught me a lot of this stuff, also if you want to get more advanced check out this post on Angular 2, Typescript, and Ionic 2.

I’m sure there are lots of best practices I missed, let me know your tips in the comments.

* Updated 9/18 to remove jQuery.ready (deprecated)

Exit mobile version