PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Eight Weeks of Prototype: Week 7, Other Prototype Functionality

Including JavaScript Code in Your Applications

Although many of the examples in this "Eight Weeks of Prototype" series have simply used JavaScript inline in a HTML file, this is not the recommended way of doing things. I have done this purely to simplify the examples so the important Prototype concepts can be discussed easily.

Your goal when developing HTML that uses JavaScript is to include absolutely no JavaScript expressions whatsoever in the HTML. All JavaScript code should be restricted to one or more external files.

Note: In some cases such as when using JavaScript-based statistics tracking code (such as Google Analytics) it is not possible to avoid doing so, but generally in these cases it's only a few lines of code.

You may recall in the previous example I recommended saving a single JavaScript class to a single file. Well, not only should you do this, but the code used to instantiate your classes should also be in an external file. Typically, the external file will observe the onload DOM event and do what is required at that time.

There are several good reasons for doing this, including:

  • Your pages will load faster in the long run because the files are smaller. Realistically the first time the external files are loaded for a single client the load time will be negligibly longer since they do not yet exist in the client's browser cache but for subsequent pages there will be an improvement. A by-product of this is that it may reduce your hosting charges if you run a busy site since data transfer will be much lower.
  • Both your HTML code and your JavaScript code will be easier to maintain since there is much less clutter.
  • Using external JavaScript files allows you to easily reuse your code.
  • Forcing yourself to use external files also forces you to think more about how best to structure your application.

To demonstrate this, I'll rewrite the example from Listing 4 in this article. In this code, a class called PersonLoader is defined, which is then instantiated in order to update the content of a div called #foo.

The first step is to move the PersonLoader class into a separate file. As I recommended in the previous article in this series, I will move this to a file called PersonLoader.js. In other words, the filename corresponds to the class name (this is discussed more in the other article). Listing 9 demonstrates this. For the sake of this example I assume the file is saved to the /js directory on your web server (the same location as prototype.js).

Note: One change I've made to this class from earlier is that it now checks for the container element. This is to prevent any JavaScript errors from occurring just in case the element wasn't found on the page.
Listing 9 Writing the PersonLoader class to a separate file (PersonLoader.js)
var PersonLoader = Class.create({
 
    initialize : function(container)
    {
        this.container = $(container);
 
        if (!this.container)
            return;
 
        var options = {
            onSuccess : this._loadSuccess.bind(this)
        }
        new Ajax.Request('listing-03.php', options);
    },
 
    _loadSuccess : function(transport)
    {
        var tpl = new Template('<strong>#{name}</strong> is from <em>#{country}</em>');
        var content = tpl.evaluate(transport.responseJSON);
 
        this.container.update(content);
    }
});

Next we define another JavaScript used to instantiate this class. In order to keep even this code structured nicely, I like to call this file Application.js in which an object called Application is defined. Technically this isn't a class, but I like to use it to group these single "utility" functions together. You can almost think of it as a static class (that is, a class that cannot be instantiated).

Since we only want the startup() method (defined below) called once the page has finished loading, we observe the window load event to ensure this. This is covered in more detail in the fourth article of this series.

Note: You don't have to structure your code in this manner; it's only my personal preference. You could just define a single "normal" function to run if you prefer, or put all the startup code in the window onload handler.

Listing 10 shows the code for the Application.js file, also saved to the /js directory. Once the page finishes loading, the Application.startup() function is called. In the startup() method, we instantiate the PersonLoader class, passing to it the name of the container.

Listing 10 The Application class, used for handling startup and holding other utility method (Application.js)
var Application = {
 
    startup : function()
    {
        new PersonLoader('foo');
    }
 
};
 
Event.observe(window, 'load', function() {
    Application.startup();
});
Note: The startup() method relies on the PersonLoader class being already loaded, meaning it must be loaded in the calling HTML. This is a slight drawback since ideally files would be loaded on demand. I have discussed a technique for dealing with this at http://www.phpriot.com/blog/dynamically-loading-javascript.

Finally, we implement the HTML code. This is now much simpler than before. All it needs to do is (in addition to loading Prototype) to load the two JavaScript files we created, and ensure the #foo element exists on the page. This is shown in Listing 11.

Listing 11 With all JavaScript moved to external files the HTML is much shorter (listing-11.html)
<html>
    <head>
        <title>Referencing JavaScript from external files</title>
        <script type="text/javascript" src="/js/prototype.js"></script>
        <script type="text/javascript" src="/js/PersonLoader.js"></script>
        <script type="text/javascript" src="/js/Application.js"></script>
    </head>
    <body>
        <div id="foo"></div>
    </body>
</html>

This technique also goes hand-in-hand with not defining DOM events inline (besides, I showed you a better way of doing this in the fourth article in the series) and also not using inline styles (rather, using external stylesheets and CSS classes).

In the final article in this series you will see this in action more and how clean doing everything in external files makes even web forms and other more complicated markup.

In This Article