Tips for writting and loading Javascript

Learn useful tricks for writing optimized JavaScript

Because we live in the era of frameworks like Vue.js, Next.js, Svelte, Solid, and Qwik, we are accustomed to writing and splitting our code in a completely different way than with client-side-only JavaScript frameworks (like jQuery) or vanilla JS.

However, if you don’t use a component-based framework that splits assets automagically, you will probably need to find your own way of loading and splitting code.

The tips mentioned in this section are focused on helping you write maintainable JavaScript in the “old school way.” They are universal, so you can use them even if you don’t use Signalize.

Selectors

Selectors we add to elements should describe what they are used for.

Let’s start with prefixes. Prefixes are good for separating “logical and state selectors” from “styling only” or “descriptive” selectors:

  • js-: Use this prefix for selectors that will be used within JavaScript => js-toggle-button. These selectors should be only in templates and JavaScript files.
  • s-: Use this prefix for setting some state of an element, e.g., s-active, s-toggled. These classes can be styled within CSS and can be set by JavaScript.

To improve searching for selectors in the project and to instantly see which classes belong together, it’s better to keep the naming in “sequence”:

  • Let’s say we have a js-tabs component.
  • Our class for a single tab will be js-tabs-tab.
  • The class for the toggle button will be js-tabs-tab-toggle not js-toggle-tab.
  • Now, if we search for js-tabs we instantly see which selectors work together in which files.

Directives such as data- can be used to “keep a state” directly on a DOM element and at the same time select an element:

  • data-js-: can be used to define, for example, data-js-tab="first", data-js-tab-toggle="first". It’s easy to understand what these directives do. Thanks to the js prefix, we know it’s going to be used by JavaScript and that the button toggles a tab with a given ID.
  • data-s: this directive can be used to maintain a state on an element, such as data-s-tab-toggled="false". This directive can also be used within CSS for styling toggled or hidden elements.

Splitting Code

Splitting assets is always a challenge.

There are various ways to load JS and CSS. The following tips work for me, so I have decided to share them:

  • Layout CSS and JS - Magic that is used anywhere.
  • Page CSS and JS - Only for one page.
  • Lazyloaded chunks after an event - dialogs, dropdowns, cart. Use Signalize resolve function, native import or embed the path to the script into the head of your page.

Individual logic should be split into files. Do not place all logic into one file. You can use one of the following tips to split the code:

Directory Structure 1 - Keep CSS and JS in separate directories:

  • assets
    • js
      • components
        • layout navigation.js
        • dropdown.js
      • layout.js
      • index.js
      • blog.js
    • css

Directory Structure 2 - Keep CSS and JS together:

  • assets
    • ui
      • components
        • layout
          • navigation.js
          • navigation.css
        • dropdown.js
        • dropdown.css
      • layout.js
      • layout.css
      • index.js
      • index.css

Use ES modules

ES modules are supported in most modern browsers and they have a lot of features:

  • They are imported and executed only once.
  • Dependencies are resolved automatically. No need for bundling.
  • With import maps, you can easily orchestrate your import paths. JavaScript frameworks like Astro don’t even need import maps in most cases.
  • It’s easy to split functionality into various ES modules and import them only when necessary using dynamic imports.
  • Etc…
<script type="module">
	import '/path/to/script.js';
</script>

Loading Code

The “basic goal” to write optimized JavaScript is to delay its loading and execution until it’s really necessary and load only what is necessary.

For example, if you have multiple carousels on your page, you might want to load the script for them after a click on a button that will trigger the slide effect to the left/right. When the script is loaded, it should only initialize those carousels that are currently in the viewport by checking visibility using intersection observer. This can be applied to any other scripts and logic => dialogs, content loaded within dropdowns and filters, etc.

Harry Roberts had a nice talk Get Your Head Straight (slides are here) where he talked about the order of elements within the head tag and how to load scripts and styles. To summarize, JavaScript and CSS should be split into three locations:

  • Head start CSS: Right after the title element. For example, for Critical CSS, Layout, and Page CSS.
  • Body after priority content JS: For example, before the footer or after the last product list. Any high-priority JS, like navigation, filter, or analytics (e.g., GTM and FB).
  • Body end: Low-priority JS. Those that the user will probably not use immediately after the page loads, like dialogs, dropdowns, and JS for content in toggleable sections.
<html>
	<head>
		<!-- Set charset -->
		<meta charset="utf-8" />
		<!-- Set viewport to prevent loading of unnecessary assets -->
		<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
		<!-- Base64 favicon, so the user sees, that something is loading -->
		<link rel="icon" href="data:image/png:base64,.." sizes="any" type="image/png">
		<title>...</title>
		<!-- CSS -->
		<!-- Custom head content -->
	</head>
	<body>
		<!-- Page content -->
		<!-- Js after priority content -->
		<!-- Other content -->
		<!-- Low priority JS at the body end -->
	</body>
</html>

Use the Web Platform

Modern browsers provide a lot of native functionality that you might want to check and consider before creating your own solution:

YAGNI & KISS principles

You Aren’t Gonna Need It + Keep It Simple, Stupid.

Think about what your clients/customers want and if the script you write is really necessary. A few examples:

  • Does the form need to be reactive, or can it be validated only before it is sent?
  • Do I need a super cool carousel with ninja animations, or am I fine with native scroll snap?
  • Is there any need for an “SPA” besides it just “looks cool”? Does any customer need that?