Javascript

Principles

JavaScript is truy an awesome language. But if not maintained and structured correctly, it can be become very unreadable and difficult to manage of debug.

DRY (Don't Repeat Yourself)

This is a basic principle within software development. It is aimed at reducing repetition of information. This basically means don't type the same functionality twice. This does not mean you never type the same code twice, it means that you should not define the same functionality of code blocks twice.

If not upheld, it will become difficult to manage the code if the logic changes. We have to make changes all over the place where we have "copied" or written the same code blocks. This is a waste of time!

To avoid having this problem you have to divide your system into pieces. Divide code and logic into smaller reusable units and use that code by calling it wherever you want. Avoid writing lengthy methods, but try to re-use existing pieces. This is very difficult and requires some thinking and preparation. If not managed correctly the consequences can be very severe (drama alert!).

The benefits are:

  • Reduces chances of bugs
  • Saves time and effort
  • Easy to maintain

KISS (Keep it simple, stupid)

Keep the code simple and clear! The code is for humans to understand, so keep it straightforward. Keep methods small and divide your code into multiple files. Each method should solve one small problem. If you have a lot of conditions in one method, break these out into smaller methods. It is easier to read, maintain but can also help you find bugs faster than the speed of light.

Before writing the actual code, think of many solutions for your problem. Then choose the simplest one. Whenever you find yourself writing lengthy code, stop for a second, rethink your life and divide it into smaller parts. Refactor all the time, don't be lazy, but iterate onto your code and don't write hacks to make it all work. If this is done correctly it will be easy to read and modify the code without writing iterative if-statements and creating this lovely pyramide effect in your code editor (😤).

This kind of affects the DRY principle as well, when do you split up and when is the code getting to complex. Find a balance yourself between these two iterating on them and improving your own code.

Class based

JavaScript does not have a pattern you have to follow to create code. This is decided by the developers themselves. You can for example either write function based or class based JavaScript. In the brandplatform it is prefered to use class based JavaScript because it is easier to initiate different components and their respective functionality. Each component has their own properties and methods, these are not possible to easily define through the use of only functions.

For example, if you have a component that should toggle collapsing content when clicking a button. You should keep in mind here that there can be multiple instances of the same component. Class based JavaScript is very handy here because we need to define an EventListener to catch the click on the button and we need a boolean represeting the state of the collapsed content. If this is done through functions we would always have to retrieve the state from the document model and that would make the JavaScript unmanagable and dirty.

Dependencies

For the brandplatform we tend to always avoid the use of dependencies because they can make things difficult for third parties. Third party scripts or libraries can be handy for components that are quite complex and appear in almost every design: I'm talking about a carousel or a datepicker. Keep the amount of dependencies as limited as possible. Do not use any library because of just one of its many features. Just one more thing: AVOID JQUERY AT ALL COSTS.

Structure

A modular, strict and clear JavaScript structure is very important. This can, by default, be found in the src/js directory. If you want this in a different folder, this can be done by updateing the configuration of the project.

Because there is no framework or no crazy library that does stuff behind the scenes we write the JavaScript ourselves and tried to create a folder structure based on our practices within this type of project.

The JavaScript structure is divided into:

  • constants: Folder containing the global variables that should be used across the different modules.
  • extends: Folder containing extendable classes. These are classes that contain functionality that is reusable across certain modules.
  • managers: Folder containing singletons. Classes that can be initialised only once. These have a single taks and make sure that the modules can for example communicate between each other.
  • modules: Folder containing the components. Each module is another folder containing an entry point (index.js) and different parts to make the module work.
  • utils: Folder containing reusable and small functions that always return something. These can be used in the different folders within the JavaScript structure to execute certain small and repeatable tasks.
  • vendors: Folder containing minified files or any other third party script to make a module work. This can be a carousel, datepicker, ...
  • main.js: The main JavaScript file where everything comes together. This should only contain initialisers for the modules and should not contain any logic.

Modules

Initialise

When creating a module there are some rules to follow to make sure a decent structure is upheld.

The module is a class and should be called and initialised in the main JavaScript file:

"use strict";

/**

  • Import
  • =============================================================================
  • / import { Example } from "./modules";

/**

  • Init
  • =============================================================================
  • / const init = () => { const example = new Example(document.querySelector(".js-example")); };

init();

As you can see in the above declaration we simply initialise the respective class and pass it the element necessary to make the module work by itself. This is a nice pattern, because this way you can select other dom elements within this querySelector and you don't have to look through the entire document structure.

Definition

Now, where is this Example class placed? Each module should be placed within its own folder in the modules folder. As an entry point the module has an index.js file:

"use strict";

/**

  • Imports
  • =============================================================================
  • /

// import ... from "./";

/**

  • Class

  • =============================================================================

  • / class Example {

    /* ====================================================================== *

    • Constructor

    • ====================================================================== */ constructor(el) { this.el = el;

      this.initBinds(); this.init(); this.initEvents(); }

      /* ====================================================================== *

    • Inits

    • ====================================================================== */ init() {}

      initBinds() {}

      initEvents() {}

      /* ====================================================================== *

    • Helpers

    • ====================================================================== */

      /* ====================================================================== *

    • Handlers

    • ====================================================================== */ }

export default Example;

As you can see in the example above we like to divide JavaScript methods in different groups because that way we define what they are supposed to do. We have 3 different types:

  • Initialisers: Once the constructor is executed we start the initialisers. These are methods that can either return something or create something. These are only executed once. In naming these methods we always prefix them with
  • init*.
  • Helpers: Helpers are methods that always return a value. They change or manipulate the value. These can be seen as utils as well, but they are more defined within their respective class-scope. They belong to that component and are specifically made to work with that component. In naming these methods we prefix them with either set or get.
  • Handlers: Handlers are methods that execute something or handle the aftermath of an action. These can be more complex than the previous types and can do anything they want. In most cases they never really return something, they simply one thing. The naming of these methods is prefixed with handle.

Expanding

Now the module can be defined. Do take note that you should divide your HTML elements into a multitude of classes that all do their own thing within that same module. For example: You have a collapsable component that should animate the content height when clicking on the button. This means that you have a module Collapse with its own index.js file. You can have another file for the button Button.js and the content Content.js. The Button.js will contain the eventlistener and the callback to the index.js to trigger the Content.js animation and behaviour.

Here is a little metaphor to hopefully make it a bit more clear:

The way we see it is that the index.js is the actual brain of the module. Button and Content are limbs that are created from that brain. Button and Content do not directly communicate with each other. But they do communicate by sending out signals (callbacks) to the brain and the brain catches these signals and will send out another signal to its different limbs. So Button and Content only communicate indirectly with each other through the brain. It is the brain that decides which limbs should receive a signal and respond to the action, derived from the Button.

Conclusion

These are some tips we have when writing JavaScript for manipulating the DOM. The developer is the one who decides how the structure is defined but try to keep it consistent, maintained and clean.

A wise man once said:

Always code like the developer coming after you is a dangerous psychopath that knows where your family lives