Categories
JavaScript Best Practices

Maintainable JavaScript — CSS and JavaScript

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at loose coupling.

Keep JavaScript Out of CSS

IE8 allows us to run JavaScript expressions in our CSS code.

This is definitely bad since we don’t want tight coupling between CSS and JavaScript.

We could write something like:

.box {  
  backgroundColor: expression(document.body.backgroundColor);  
}

This is IE8 specific and it introduces tight coupling, so it should never be used.

CSS is reevaluated frequently and so the JavaScript will run frequently, so we shouldn’t put JavaScript code in the CSS.

It’s also hard to debug since the JavaScript code is in the CSS file, so there’s no way to trace them in any browsers.

Keep CSS Out of JavaScript

We can set CSS styles with JavaScript in any browser.

They work well together.

However, changing styles in JavaScript means that JavaScript code has to be run.

So if we have code like:

element.style.color = "green";

or

element.style.color = "red";  
element.style.left = "10px";  
element.style.top = "200px";  
element.style.visibility = "visible";

then we have to run one or more operations to change the style of one element.

This is also a problem because if we have style problems, we can’t just fiddle with the CSS to fix it.

We have to step through the JavaScript code to find the issue and fix the code.

This is definitely slower than just turning on and off the CSS and changing the values.

Another bad way to change the CSS is to change the cssText property of the element.

For instance, some people may write:

element.style.cssText = "color: green; left: 10px; top: 100px; visibility: hidden";

This is also bad because the styles are all in a string.

Therefore, we can’t just toggle them on and off and changing their values without changing the code and refreshing.

Keeping CSS out of JavaScript means that all the style information lives in the CSS.

So we write something like:

.box {  
  color: red;  
  left: 10px;  
  top: 100px;  
  visibility: visible;  
}

in our CSS files.

This way, we can fiddle with them in the browser dev console instead of changing the code and refreshing.

Also, the coupling is looser since the CSS is self-contained.

In our JavaScript code, we can toggle on the class by writing:

element.className += " box";

or:

element.classList.add("box");

And we can remove them by:

element.classList.remove("box");

CSS class names are the communication mechanism, between CSS and JavaScript.

JavaScript is used for adding and removing class names from elements throughout the lifecycle of the page.

The styles are applied by the classes un the CSS code.

Now we only need to change the CSS instead of changing manipulating styles directly.

Conclusion

We should keep JavaScript out of CSS and CSS out of JavaScript.

This way, we won’t have messy code tightly coupling code that are hard to debug.

Categories
JavaScript Best Practices

Maintainable JavaScript — Config Data

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at ways to externalize config data.

Good Ways of Detecting Properties

We can use the in operator to check if an object property exists in an object.

For instance, we can write:

const object = {
  count: 0,
};

if ("count" in object) {
  // ...
}

to check if the count property is added to the object object.

The expression in the if block heading will return true so the block will run.

This checks if the count property is in the object itself and whether it’s in its prototype chain.

To check if a property is a non-inherited property of an object, we can use the hasOwnProperty method.

For instance, we can write:

const object = {
  count: 0,
};

if (object.hasOwnProperty('count')) {
  // ...
}

It returns true if 'count' is an own property of object and false otherwise.

If we aren’t sure whether hasOwnProperty exists in object , we can write:

if ("hasOwnProperty" in object && object.hasOwnProperty('count')) {
  // ...
}

Now we know for sure that hasOwnProperty exists before calling it.

Separate Configuration Data from Code

Configuration data is any hardcoded values in our app.

If we have:

function validate(value) {
  if (!value) {
    console.log("Invalid value");
    location.href = "/errors/invalid";
  }
}

then we have 2 pieces of config data in our code.

One is the 'Invalid value' string.

And the 2nd is the '/error/invalid' URL string.

URLs and messages may change, so we may separate them so that we can define one reusable variable for each and then reference that everywhere else.

Data that are configuration data include:

  • URLs
  • Strings that are displayed in the UI
  • Repeated unique values
  • Settings
  • Any value that may change

We don’t want to modify multiple parts of our source code just to change some config values.

Externalizing Configuration Data

The first step to separating configuration data from our code is to externalize the config data.

This means getting the data out of the middle of our JavaScript code.

Instead of what we have before, we instead write:

const config = {
  MESSAGE_INVALID_VALUE: "Invalid value",
  URL_INVALID: "/errors/invalid.php",
};

function validate(value) {
  if (!value) {
    console.log(config.MESSAGE_INVALID_VALUE);
    location.href = config.URL_INVALID;
  }
}

We created a config object which has the config data in its own location,

Then we referenced it in our code.

Each property in config is a piece of data.

The property is uppercase so that we can tell them apart from other properties.

The most important part is externalizing the data,.

The rest is up to our own preference.

Conclusion

Config data is hardcoded data that are used in multiple places.

We should externalize our config data so that we can use them in multiple places without repetition.

This way, we can change it once and don’t have to worry.

Categories
JavaScript Best Practices

Maintainable JavaScript — Comparisons and Code Strings

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at comparisons and eval.

Boolean Comparisons

Boolean comparisons with == and != also do things we don’t expect.

This is because any operands are converted to true or false before comparison is done.

For instance, if we have:

console.log(1 == true);

then we get true because 1 is converted to true .

On the other hand, if we have:

console.log(0 == false);

we also long true since 0 is falsy and it’s converted to false .

Object Comparisons

If objects are compared then, it’s converted to a primitive value with the valueOf method.

If valueOf isn’t present, then toString is called instead.

Then the comparison continues like the other kinds of comparisons.

For if we have:

const object = {
  toString() {
    return "0xFF";
  }
};
console.log(object == 255);

Then console log logs true since object is converted to a string with the toString method.

Then the Number function converts that to a decimal number.

The null and undefined Values

Also, type coercion occurs between null and undefined is also done.

They’re deemed equivalent by the == operator.

For instance, if we have:

console.log(null == undefined);

then that logs true .

=== and !==

Type coercion is done with == and != , so we shouldn’t use them for comparisons.

The coercion rules are too complex.

Instead, we should use the === and !== operators, which don’t do any type coercion.

So if we have:

console.log(5 === "5");

we log false .

And:

console.log(25 === "0x19");

also logs false .

And also if we have:

const object = {
  toString() {
    return "0xFF";
  }
};
console.log(object == 255);

that also logs false because there’s no type coercion done.

Using === and !== is recommended by Doug Crockford’s style guide, Google style guide, etc.

This is almost a universal recommendation between all style guides.

Linters like ESLint and JSHint all warn us about the use of the == and != operators.

eval()

eval is a function that takes a string of JavaScript code and runs it.

For instance, we can write:

eval("console.log('hello')");

to log 'hello' in the console log.

The eval function isn’t the only function that runs JavaScript code from a string.

The setTimeout and setInterval functions also takes a function and runs that code.

The Function also takes strings with parameters and code and returns a function.

Running code from a JavaScript string is universally considered to be a bad practice because of the security issues from running code that can potentially be run from any source.

Also, JavaScript code inside a string can’t be optimized for performance with JavaScript engines.

Therefore, we shouldn’t use the eval functuon or Function constructor.

Also, we should use setTimeout and setInterval with actual code instead of a string.

For instance, we use it properly by writing:

setTimeout(() => {
  document.body.style.background = 'red';
}, 50);

setInterval(() => {
  document.title = `the tiome is ${new Date()}`;
}, 1000);

We run our code in a callback instead of running code from a string.

Conclusion

We should use === and !== for comparisons.

Also, we shouldn’t run code from a string.

Categories
JavaScript Best Practices

Maintainable JavaScript — App Logic and Primitive Value Check

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at separating app logic from event handling.

Also, we look at how to check for primitive values.

Event Object

When we’re handling events, we should keep the event object in the event handler only.

This will reduce coupling in our code.

So if we need to use something from the event object, we keep them in the event handler.

For instance, we can write:

function onclick(event) {
  const {
    clientX,
    clienty,
    preventDefault,
    stopPropagation
  } = event;

  preventDefault();
  stopPropagation();
  showPopup(clientX, clienty);
}

function showPopup(x, y) {
  const popup = document.getElementById("popup");
  popup.style.left = `${x}px`;
  popup.style.top = `${y}px`;
  popup.className = "popup";
}

const button = document.querySelector('button');
button.addEventListener('click', onClick);

We called the preventDefault and stopPropagation methods in from the event object in the event handler.

This is much better than passing the event object and call it in that function.

We don’t want our app logic code and event handling code to mix.

This way, we can change the app logic and event handling code independently.

And we can call the app logic code directly.

So we can write something like:

showPopup(10, 10);

It also makes testing a lot easier since we can call showPopup directly to test it.

Avoid Null Comparisons

A common problematic pattern in JavaScript code is testing variables against null .

For instance, we may have code like:

function process(items) {
   if (items !== null) {
     items.sort();
     items.forEach((item) => {
       //...
     });
   }
 }

We have the process function that checks the items against null .

Then we assume items is an array and call sort and forEach on it.

Comparing against null doesn’t mean we made sure that it’s an array.

We only know that it’s not null .

We can improve our value comparison so that we can actually check for what we want.

Detecting Primitive Values

There’re a few types of primitive values in JavaScript.

They include bigints, strings, numbers, booleans, null and undefined .

The typeof operator is the best one for checking for types of primitive values.

We just need to pass in the expression we want to check as the operand.

For strings, typeof returns 'string' .

For numbers, typeof returns 'number' .

For boolean, typeof returns 'boolean' .

For bigints, typeof returns 'bigint' .

And for undefined , typeof returns 'undefined' .

The basic syntax for typeof is:

typeof variable

We can also write:

typeof(variable)

However, the 2nd way makes tyepof looks like a function.

Therefore, we should use it without parentheses.

We can code defensively bu using the tupeof operator to check the type of the value of the expression.

For instance, we can write:

if (typeof name === "string") {
  let part = name.substring(3);
}

to check a string.

We can write:

if (typeof count === "number") {
  setCount(count);
}

to check for and set a number.

And:

if (typeof canShowMessage === "boolean" && canShowMessage) {
  showMessage("hello");
}

to check for a boolean and whether it’s true or not.

And we can write:

if (typeof app === "undefined") {
  app = {
    // ....
  };
}

to check for undefined .

Conclusion

We shouldn’t make our app logic depend on the event object.

Also, checking for null isn’t good enough for checking most types of data.

Categories
JavaScript Best Practices

Maintainable JavaScript — Accidental Globals

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at avoiding accidental creation of global variables.

Avoiding Accidental Globals

We should avoid accidental global variables.

If we’re writing JavaScript scripts, then we’ll create global variables by default if we assign a value to variables without using any keyword.

For instance, if we have:

count = 10;

then count is a global variable.

If we have a linter like JSHint or ESLint, then we’ll see a warning if we have something like that.

Also, strict mode will prevent us from creating global variables accidentally.

So if we have:

'use strict';
count = 10;

then we’ll get an error.

If we run the code above, we get ‘Uncaught ReferenceError: count is not defined’.

Strict mode is available in almost all modern browsers so we should use it.

Modules have strict mode on by default, so we’ll always get the error if we try to create new global variables.

Existing global variables should be treated as read-only.

We shouldn’t add any new properties to them to avoid errors.

For instance, if we use global variables like window or document , then we shouldn’t set any properties to them.

If we work with older code, we should update them whenever we can and enable strict mode.

One Global Object

Many libraries provide us with their own global objects that we can use in our code.

jQuery has the $ and jQuery objects.

The latter is added for compatibility with other libraries that use $ .

Vue has the Vue global variable to let us create a new Vue instance.

We create one global object with a unique name so that it’s unlikely that it’ll clash with other libraries in the app.

For instance, we may create our own constructor by writing:

function Person(name) {
  this.name = name;
}

Person.prototype.speak = function(speech) {
  console.log(`${this.name}: ${speech}`)
};

const james = new Person("james");
const mary = new Person("mary");
const jane = new Person("jane");

We create a Person constructor with the speak prototype method.

It takes the name parameter and assigns that to this.name .

Also, it has the speak instance method.

Then we can use it with the new operator.

This creates many global-scoped variables.

Instead of putting them all in the global scope, we put them in an object so that they aren’t global anymore.

For instance, we can write:

const obj = {};

obj.Person = function(name) {
  this.name = name;
}

obj.Person.prototype.speak = function(speech) {
  console.log(`${this.name}: ${speech}`)
};

const james = new obj.Person("james");
const mary = new obj.Person("mary");
const jane = new obj.Person("jane");

We put our Person constructor in the obj object so that the Person constructor isn’t in the global scope.

This way, we won’t be able to accidentally change it or overwrite it.

Conclusion

We put our code in an object so that they can’t be in the global scope.

Also, accidental global variables should be avoided with strict mode.