russian version

Modularity in JavaScript, dynamic load

Issue

Quite often I have a task to create create JavaScript component (below just component) and put it in different parts of a site. As a rule, that results in file in server language which:

  • puts the component
  • provides connection of necessary JavaScript files
  • controls the order of files connection
  • disables the load of one and the same file for several times

Everything seems to be good, but there are some unpleasant moments:

  • you are to create server functionality for each new component
  • to maintain several projects on different server technologies, the functional, described above, is to be realized (i.e. doubled) on each of them

All that takes time and effort of several people.

Solution

Before changing anything, you are to understand what you finally want to have. If you put yourself in the place of a programmer, probably you’ll feel like marking the location of component on the page and not caring about JavaScript files. Being on a client’s side, it’s necessary to understand the type and location of the component on the page and to realize the last three items from the first list, i.e. do the following:

  • find the component on the page
  • define the type of the component
  • provide connection of necessary JavaScript files
  • control the order of file connection
  • disable the download of one and the same file for several times

Tag as a component

I always put component’s mark to any node of the tree. I’ve never had such situations when that was impossible. Moreover, if something goes wrong with JavaScript, this node will be able to maintain minimal required functional.

After a great number of experiments I understood that the best is to mark a node with a particular class name. From my own experience I also realized that you shouldn’t be attached to this class name in CSS.

<div class="component"></div>
        

Now it’s easy to find all the nodes that are components. Any modern JavaScript library supports CSS selectors.

var components = getElementsByCSSSelector('.component');
        

I don’t advise any concrete library intentionally, emphasizing the fact that these methods are not attached to any. I should say, it’s even possible to go without exterior libraries. Friendly speaking, by the time of writing this article, I stopped using my methods of components search and began using prototype. But I suppose jQuery or base2, or something else will also perfectly cope with the task.

Component type

Having found the component, it’s necessary to understand its type. To my mind, the simplest and the most convenient way to show a component type (thanks to Vitaly Harisov) is to transmit hash object to onclick attribute.

<div class="component" onclick="return {type:'Type'}"></div>
        

On initializing the node of the tree a browser transmits the value of onclick attribute to onclick method. Now we can evaluate an onclick method of the node found and get hash object with all the information required.

var node = components[index];
var type = node.onclick().type;
        

You can transmit initializing parameters of the component near the type.

<div class="component" onclick="return {type:'Type', params: { ... }}"></div>

JavaScript files load

I won’t tell about methods of dynamic load of JavaScript files in details. The main difficulties are seen in old browsers, in latest versions of popular browsers works the following code:

createScript = function (charset, src){
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('charset', charset);
    script.setAttribute('src', src);
    // InsertBefore for IE.
    // IE crashes on using appendChild before the head tag has been closed.
    var head = document.getElementsByTagName('head').item(0);
    head.insertBefore(script, head.firstChild);
}

Having found the component and defined its type, Java Script functionality should appear on the page. It will make the component work. If you know component type, you can load the file that describes its behavior. But in modern reality component’s functions form several files, not one. The final file, which describes the functions, depends on libraries that are common for several components. Libraries, in turn, may depend on other libraries and so on. To create a mechanism of components functional load I had to realize the following capabilities for a file with any functionality:

  • set a list of dependence on other files
  • receive a notice about necessary files load
  • signal when load and initialization complete

In general case each file should have the following capabilities:

require(
    // indicate dependence on other files
    ['file1', 'file2' ...],
    // receive a notice about their load(execute function)
    function(){
        // initialization
        // signal when load and initialization complete
        loaded('file');
    }
);

When load and initialization complete, each of the files listed should entail function loaded with its name as an argument. The function require should wait till all the files from the list ['file1', 'file2' ...] entail loaded and then entail the transmitted function function. If a file was once loaded or is being loaded, you shouldn’t load it again.

In this article I won’t give concrete examples of functionality. As I stated above, realization may greatly depend on surroundings and libraries used. At the end of the article you may find links to my latest realization.

Result

So, now you can find a component, define its type, load all the files that realize its functional and initialize it.

var components = getElementsByCSSSelector('.component');
var node = components[index];
var component = component.onclick();
require(
    [getFileName(component.type)],
    function(){
        window[getObjectName(component.type)].init(node, component.params);
    }
);

In my realization each file has a unique name, which can be translated either into path to JavaScript file or into the name of the object which is in this file (functions getFileName or getObjectName).

Finally, being on server side, you should only mark the node of a tree as a component and indicate its type. A client will accomplish all the JavaScript files load logic and initialization. This solution easily passes from project to project, from one server language to another.

Examples

Example from life is here: http://jsx.ru/Texts/ModulesInJS/example.html.
It’s a bit more complicated because of aliases, but I will obligatory write about them later.
The file that loads scripts dynamically is here: http://jsx.ru/scripts/b/1.1.0/jsx.js.
The file that finds components on page is here: http://jsx.ru/scripts/b/1.1.0/Components.js

You may discuss the article here: http://andrewsumin.livejournal.com/13127.html?view=84551