12 common JavaScript questions answered

11. How can I use JS to improve the performance of my site?

At a time where the majority of web browsing is done on a phone or a tablet, performance is critical. Not everybody has the latest device, and each delay and stutter could cost a customer. Thankfully, there are plenty of ways to use JavaScript to stop this happening.

Keep it passive

Jagged scrolling is a clear sign something is up. In some cases, the browser is being forced to wait because of listeners applied to the page. Events such as 'wheel' or 'touchmove' are able to cancel scrolling, so the page has to wait until the event has completed before the default scrolling behaviour can begin. 

This can cause jerky and inconsistent scrolling, which makes for a poor user experience.

document.addEventListener('touchmove', handler, {passive: true});

To get around this, pass an object as the third parameter when adding an event listener. By marking the event as passive, the browser can assume scrolling will not be affected, so it can start immediately. 

This third parameter replaces the 'useCapture' option in older browsers, so it is important to use feature detection when making use of this type of listener. To intentionally disable scrolling, applying 'touch-action: none' in CSS will help in more browsers.

Throttling events

Events like scrolling and resizing fire as quickly as they can to make sure whatever is listening can stay up to date. If something resource intensive is happening on each event, this can quickly grind a page to a halt.

const resizeDebounce = debounce(() => {
// Code for resize event }, 200);
window.addEventListener('resize', resizeDebounce);

Debouncing is a technique that throttles how often the callback to one of these events is called. The implementation of a debounce function and how often the function gets called will vary by project, but reducing the events to five times a second, for example, will see an instant improvement on the page.

Focus on the viewport

A common use of the scroll event is to detect when an element comes into view on the page. Even with debouncing, calling getBoundingClientRect() requires the browser to reanalyse the layout of the entire page. There is a new browser API called IntersectionObserver, which reports on the visibility of observed elements by calling a function whenever they enter or exit the viewport. For infinite scrolling sites, this can be used as a flag to remove or recycle older views.

IntersectionObserver is available in all the latest browsers except Safari. It's worth using this API and falling back to older techniques, as the difference is vastly noticeable.

Separate expensive work

When working with large datasets or processing big files such as images, JavaScript can quickly lock up the browser window. All the work is getting performed on a single thread, so if that thread is busy the interface cannot update.

If you know a process is going to take a long time to run, it is a good idea to put it inside a web worker. These are scripts that run on a separate thread, which leaves the user interface running smoothly. Scripts can talk to each other through a special message method. Web workers don’t have access to the DOM and some properties on the window object, so these messages can be used to pass the necessary information.

12. How can I future-proof my JavaScript code?

One of JavaScript's core principles is that it always tries to avoid breaking changes where possible. The vast majority of code written today will still function a decade later, even in this ever-changing industry.

Just because a block of code runs, however, does not mean it's future-proof. Will your codebase still make sense a few years later?

Avoid spaghetti code

When first creating a project, it can be tempting to write everything together. While that makes it explicit what each block of code is doing, it also means all behaviour is coupled together as well. If another area of the project needs that functionality, it will either be copied or rewritten. If bugs are found or a new feature is added, each version will need updating individually, which can be a time-consuming process.

By keeping code modular, functionality can be included where it's needed. Once it's been written, any changes are instantly available. Techniques such as the module pattern can be implemented without affecting the rest of the project, which makes them easier to implement.

Be framework agnostic

Many modern frameworks, like React or Polymer, encourage developers to keep things modular through the creation of components. Each has its own way of communicating between other parts of the project. 

What happens when the next best framework arrives? Switching – or even just updating – frameworks can be a laborious task. Finding new ways for these old components to work together can eat up precious time.

Where possible, use native JavaScript features to achieve results. This way, when changing frameworks these points of friction are minimised. For example, use objects to handle data manipulation before it gets handed to a component.

This also helps when writing universal JavaScript. By avoiding using browser-based APIs where possible, code can be reused both in the browser and on the server, inside Node. 

Clean up

Once modules are written, keep them clean. Anybody reading through them should understand its functionality to speed up debugging.

let activeUsers = users.filter(user => user.active === true);

Self-documenting code is a powerful tool to future-proof your code. Using descriptive names for variables in iterators, for example, can be clearer to read than generic names like 'i'.

Most importantly, be consistent. By sticking to a uniform style, all code becomes readable. Use a style guide to define how code should look and use tools like ESLint to enforce it.

Work at scale

Being readable also extends to project structure. Without one, things can get overwhelming very quickly.

When first starting out, having files in one directory keeps things simple. When scripts import modules, there is no confusion as to where they are located.

As projects age, large collections of files can get lost in the masses. Keep a structure that scales well, such as storing all modules that deal with users in a 'users' directory. Ultimately, the best structure will depend on the project. For a single-page app, keeping model, view and controller logic separate is a must.

Keep things testable

Periodically run tests with tools like Jest to ensure all is working well

Frameworks like React encourage creating small, reusable components. Even with a scalable project structure, it can be hard to be sure they all still work correctly. By writing tests, projects can be deployed with confidence.

Unit tests will work at a module level. Tools like Mocha and Jest let developers define an expected output for a given input. Periodically running these tests can make sure there are no side effects.

Modules need to be written in a way that means they can be tested in isolation. This means having as few external dependencies as possible and not relying on global state.

There's far more to project testing, such as integration and functional testing. These should cover as much of a project as possible to keep code working long into the future.

Language of tomorrow

Make sure your code works in older browsers by using a transpiler like Babel

The best way to future-proof code, however, is by writing it in the syntax of the future. While this might sound like a step into the unknown, there are tools that make it as easy as possible.

Babel is a transpiler, which is a tool that converts one form of JavaScript to another. This is used to turn modern code into formats older browsers and environments can understand.

ES2015 brought lots to JavaScript that can help write clean code, such as arrow functions, promises and native modules. The latest standard – ES2017 – brings even more convenience through async functions Given the right presets, Babel can help convert all these into code to use today.

Eventually, projects will be able to skip the transpilation step altogether. But for now, they are a must to keep code future-proofed.

This article originally appeared in Web Designer issue 265. Buy it here.

Related articles: