Lesson 5: Modules & organization
How to organize JavaScript code into reusable modules for better readability and maintainability.
Learning goals
- Understand why organizing code into modules helps readability and reuse.
- Learn what ES modules are and how to use
<script type="module">. - Create and import small helper modules: `shared/dom.js` and `shared/storage.js`.
- Practice moving functions into modules and importing them.
Why organize code?
Good organization makes code easier to read, test, reuse and change. Splitting code into small modules:
- Reduces file size and cognitive load.
- Lets you reuse helpers across pages.
- Makes responsibilities explicit (one module = one concern).
What is a module?
A module is a JavaScript file that exports values (functions, constants, objects) and other files import them. In modern browsers you can use ES modules natively.
How to use ES modules in the browser
- Use
<script type="module">in your HTML. - Export what you need from a file with
exportorexport default. - Import using a relative path:
import { fn } from '../shared/dom.js'.
Important
- Module imports are resolved relative to the importing HTML or JS file.
- When opening files with
file://some browsers block module imports due to CORS; run a local server instead (example below).
<!-- example in your page -->
<script type="module" src="../lessons/javascript-lesson-05.js"></script>
<!-- or inline -->
<script type="module">
import { qs } from '../shared/dom.js';
console.log(qs('h1').textContent);
</script>
Example helpers (practical)
Create a shared folder at the site root and add two files: shared/dom.js and shared/storage.js. Below are minimal, practical implementations.
shared/dom.js
// exports: qs, qsa, on, createEl
export function qs(selector, root = document) {
return root.querySelector(selector);
}
export function qsa(selector, root = document) {
return Array.from((root || document).querySelectorAll(selector));
}
export function on(target, event, handler, options) {
const el = (typeof target === 'string') ? qs(target) : target;
if (!el) return () => {};
el.addEventListener(event, handler, options);
return () => el.removeEventListener(event, handler, options);
}
export function createEl(tag, attrs = {}, children = []) {
const el = document.createElement(tag);
Object.keys(attrs).forEach(k => {
if (k === 'text') el.textContent = attrs[k];
else el.setAttribute(k, attrs[k]);
});
if (typeof children === 'string') el.innerHTML = children;
else children.forEach(c => el.appendChild(typeof c === 'string' ? document.createTextNode(c) : c));
return el;
}
shared/storage.js
// exports: loadJSON, saveJSON
export function loadJSON(key, fallback = null) {
try {
const raw = localStorage.getItem(key);
return raw ? JSON.parse(raw) : fallback;
} catch (e) {
return fallback;
}
}
export function saveJSON(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (e) {
// could be quota error
}
}
Importing the helpers
From this lesson file (in lessons/) import with a path that reaches the root shared/ folder:
<script type="module">
import { qs, on } from '../shared/dom.js';
import { loadJSON, saveJSON } from '../shared/storage.js';
const prefs = loadJSON('prefs', { theme: 'light' });
console.log('prefs', prefs);
on('button#save', 'click', () => {
saveJSON('prefs', { updated: Date.now() });
});
</script>
Keep functions small
Each exported function should do one thing (single responsibility). Small functions are easier to test and reuse.
Common mistakes
- Wrong import path: remember imports are relative to the importing file. From
lessons/toshared/use../shared/.... - Forgetting
type="module"on the <script> tag; without it imports won't work. - Opening via
file://may block module imports — run a local server (example below).
Run a quick local server (recommended)
# Python 3
python -m http.server 8000
# Then open http://localhost:8000/lessons/javascript-lesson-05.html
Practice tasks
- Move a UI helper (for example a function that toggles a class on an element) from this page into
shared/dom.js, export it, and import it back. - Create a small module
shared/math.jsthat exports aclampfunction and import it where needed. - Refactor a long function into two small functions and export the smaller pieces.