JavaScript Modules

JavaScript programs started off pretty small — most of its usage in the early days was to do isolated scripting tasks, providing a bit of interactivity to your web pages where needed, so large scripts were generally not needed. Fast forward a few years and we now have complete applications being run in browsers with a lot of JavaScript, as well as JavaScript being used in other contexts (Node.js, for example).

It has therefore made sense in recent years to start thinking about providing mechanisms for splitting JavaScript programs up into separate modules that can be imported when needed. Node.js has had this ability for a long time, and there are a number of JavaScript libraries and frameworks that enable module usage (for example, other CommonJS and AMD-based module systems like RequireJS, and more recently Webpack and Babel).

The good news is that modern browsers have started to support module functionality natively, …[11]

Therefore we say that a module in JavaScript is a piece of code written to be used in various contexts, browser or serverside, and easily incorporated where needed. Modules are written into separate js files.

Example 32.1. An EcmaScript Module, first.js
'use strict';
/*
 * first.js
 */
export function doSomething(text) {
    const elem = document.createElement('p');
    elem.textContent = text;
    document.body.appendChild(elem);
}

Example 32.2. User of an EcmaScript Module, first.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Basic JavaScript Module Example</title>
        <meta name="viewport" content="width=device-width, minimum-scale=1.0">
    </head>
    <body>
        <header>
            <h1>JS Modules in HTML5</h1>
        </header>
        <script type="module">               // import, requires type="module"
            import {doSomething} from './first.js';
            doSomething('Modules rock!');    // imported function in action
        </script>
    </body>
</html>

Click here!


A word of warning: The file extension .mjs is a mnemonic for JavaScript module files, just as .js is a mnemonic for JavaScript files. For modules to work the module file(s) must be referenced from HTML5 with

<script type="module" ...

ie with a type specification of module. If you decide to use the recommended file extension .mjs you must make sure that your web server is configured to handle .mjs files in the same way as it handles .js files. The web server must do this by serving module files with a mime type of application/javascript. This is done quite simply by adding the second of the following two lines to the configuration file mime.types.

application/javascript                          js
application/javascript                          mjs

Unfortunately the vast majority of hosting sites have not yet updated their mime.types. Please nag them until they have. Until then, you have to use the .js extension at the cost of self documenting code.

A Slightly More Complex Example

The main benefits of using modules, is that HTML5 files do not need script elements for all the necessary JavaScript files. Indeed how should the HTML5 writer know in detail what is required. One is required, of course, and that one must be referenced in the HTML5 and will, in turn, import the others as needed. Let us illustrate that, and some more points, with another example. The scenario of this example is filling some available screen real estate with some content.

Example 32.3. The JavaScript Module, maintest.js
'use strict';

const $ = function (foo) {
    return document.getElementById(foo);
};

const $q = function (foo) {
    return document.querySelector(foo);
};

const greeting = function (bar) {
    return `Hello ${bar}!`;
};

export {$, $q, greeting};

Example 32.4. The Execution Code, maintesttest.js
'use strict';

import {$, greeting} from './maintest.js';

const greet = function () {
    let here = $('main');
    let g = document.createTextNode(greeting('world'));
    here.appendChild(g);
}

window.addEventListener('load', greet);

Example 32.5. The HTML5 Document, indextest.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Basic JavaScript module example</title>
        <style>
            canvas {
                border: 1px solid black;
            }
        </style>
        <script type='module' src="./maintesttest.js"></script>
    </head>
    <body>
        <header>
            <h1>Modules in HTML</h1>
        </header>
        <main id='main'></main>
        <footer>
            &copy; nml
        </footer>
    </body>
</html>

Click here!


You will notice the essential aspects:

  1. The module defines some functions just as you are used to.
  2. It exports the functions it wants to share, not necessarily all the functions should be shared. Some might be for internal use in the module.
  3. The JavaScript to be executed in the page imports the required functions from a module, not necessarily all; only what is to be used here.
  4. The HTML5 references only the execution code. That code in turn knows what to import to do its job.

A More Complex Example

This example is more complex in that it contains more modules imported into the HTML5

Example 32.6. The Keyboard Saver $ in nQuery.js
'use strict';
/**
 * nQuery, *the* JS Framework
 */
export const $ = function (foo) {
    return document.getElementById(foo);    // save keystrokes
}

Example 32.7. The Code that Builds the Board, buildTttBoard2.js
'use strict';
/**
 * buildTttBoard - Creates TicTacToe Board
 */
import {$} from './nQuery.js';

let buildTttBoard = function() {
    const DIM = 3;
    let div = $('ttt');                               // points to id: ttt
    let t = document.createElement('table');
    t.style.border = '1px solid black';
    t.style.height = '96%';
    t.style.width = '96%';
    t.style.margin = '1% auto';
    for (let i = 0; i < DIM; i++) {
        let r = document.createElement('tr');
        for (let j = 0; j < DIM; j++) {
            let c = document.createElement('td');
            c.style.textAlign = 'center';
            c.style.verticalAlign = 'middle';
            c.style.fontWeight = '900';
            c.style.fontSize = '40px';
            c.style.border = '1px solid black';
            c.style.height = '32%';
            c.style.width = '32%';
            c.addEventListener('click', function eh(e) {
                e.target.appendChild(document.createTextNode('X'));
                e.target.removeEventListener('click', eh);
            });
            r.appendChild(c);
        }
        t.appendChild(r);
    }
    div.appendChild(t);
}

export {buildTttBoard};

Example 32.8. The Execution Code in the PagethePage.js
'use strict';
/**
 * createPage - Creates page content on load of minimal html
 */
import {$} from './nQuery.js';
import {buildTttBoard} from './buildTttBoard2.js';

let createPage = function() {
    let b = $('bod');                               // points to body
    let h1 = document.createElement('h1');
    let h1t = document.createTextNode('Some Page');
    h1.appendChild(h1t);
    h1.setAttribute('style', 'color: yellow');        // style one way
    b.appendChild(h1);
    let j = Math.floor(Math.random() * 9 + 1);
    for (let i = 0; i < 9; i++) {
        let d = document.createElement('div');
        d.style.border = '1px solid blue';          // style another way
        d.style.backgroundColor = 'silver'
        d.setAttribute('class', 'fl');              // ref to stylesheet
        if (i === j) {
            d.style.backgroundColor = 'yellow';
            d.setAttribute('id', 'ttt');
        }
        b.appendChild(d);
    }
    buildTttBoard();
}

window.addEventListener('load', createPage);

Example 32.9. The HTML5 Document, indextest.html
<!doctype html>
<html>
    <head>
        <title>What Is This?</title>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="styles.css" rel="stylesheet"/>
        <script type='module' src="./modules/thePage.js"></script>
    </head>
    <body id='bod'>
    </body>
</html>

Click here!