Modularity in Websites

Modularity: The Big Picture

Today, we'll be looking at several ways to make your website more modular — i.e., created out of reusable parts that make the website easier to maintain.

Reusability

Reusability has become one of the watchwords of the environmental movement, and for good reason. Reusing objects that have taken much time, energy, and other resources to create is eminently sensible.

Reusability makes a lot of sense in the programming world, too. Suppose that a programmer has spent a lot of time developing code that solves a problem. This code can often be reused by the same programmer in a different program, or by other programmers in their programs.

In JavaScript, we've seen that functions are a good way to make code reusable. Once code is encapsulated in a function, that function can be invoked in many different contexts.

Groups of related functions and data structures are often collected together in reusable units called modules or libraries that are used by large communities of programmers. These play an important role in the adoption of programming languages. Languages like Java, C/C++, Fortran, and Python have become popular in large part because of the impressive modules/libraries that have been built for these languages. jQuery is a library that makes JavaScript programming easier.

Modules are particularly reusable when they have been designed to be used in mix-and-match ways — think Legos, standard parts from a hardware store, USB peripherals, mix-and-match clothing, etc. That's the idea behind the logo at the top of this section. Crafting a module that is easy and useful to reuse is more art than science, but it's a worthy goal.

As a concrete example with web pages, suppose that you've figured out how to make a beautiful navigation bar for one page in a website. You'd like a way to package up your navbar code into some sort of module that you can reuse on other pages for the same site, and perhaps for pages in other sites. What you don't want to do is simply copy & paste your navbar code into other pages, because that's a nightmare to maintain. Which brings us to the topic of ...

Maintenance

What is maintenance and why is it important? We all know what, say, car maintenance is: changing the oil, keeping the tires inflated, fixing stuff that breaks. It keeps your car running better and longer. But that doesn't have anything to do with web sites, since they don't have physical parts that wear out.

But they do need to be changed and updated from time to time, which computer scientists have come to call maintenance. Countless studies have shown that, over the lifetime of a software project, about two-thirds of the overall cost is in maintenance. Surprising, but true. You may not be maintaining your cs110 project, but someone will, and we should make their job easier.

You're already doing some of the most important steps:

The techniques we will see today are similar to the idea of an external style sheet, because they allow us to put shared code in a single file that is used by more than one page. This general idea is known by computer scientists as modularity: a module is any kind of distinct, separable thing that can be used more than once.

JavaScript Code Files

As we saw before (see JavaScript files), very often, you have some JavaScript code that you'd like to use on several web pages. The best example would be a collection of useful functions, all stored in a single file. You could have the browser load the file of functions, and then your web page could invoke the particular functions you want. Let's look at some examples.

Accessibility Bar

The top of this page features a set of font sizes formatted in various sizes, where clicking on them changes the font-size for this page to the selected size. We're calling this the accessibility bar (like a navigation bar, but with accessibility controls). All the code is in a separate file, so the only thing we had to do to put the accessibility bar on this page was the following:

<script src="accessibility-bar-jq.js"></script>

Let's look at the contents of the accessibility-bar-jq.js file.

Some key things to look at and think about are:

Exercise: Accessibility Bar

How flexible are the functions defined in the file above? Could you easily use them to make just a pair of increase/decrease controls, omitting all the other controls? How?

Tax Rate Functions

Another example is some JavaScript that processes a form. Here's an example:

What is the bill?

tax:

total:

  <form id="tax_form" action="/cgi-bin/dump.cgi" method="get">
    <p>What is the bill? <input type="text" name="bill">
    <p>tax: <input type="text" name="tax">
    <p>total: <input type="text" name="total">
    <p><input type="submit">
  </form>
...
<script src="tax-calculations.js"></script>

The JavaScript code associated with this form is the following:

function newbill() {
    var bill = parseFloat($("#tax_form [name=bill]").val());
    var tax = calculateTax(bill);
    var total = bill+tax;
    // store into the form
    $("#tax_form [name=tax]").val(tax.toFixed(2));
    $("#tax_form [name=total]").val(total.toFixed(2));
}

$("#tax_form [name=bill]").change(newbill);

(The change() method is an event that happens whenever the value in the form changes.)

As you've probably guessed, the taxRate variable and the calculateTax function are defined in the external JavaScript file, tax-calculations.js.

The latter part of this example assumes that the form, in addition to the bill_amount input, also has inputs named tax_amount and total_amount. When the user fills out the bill amount, the JavaScript code calculates the other two. It puts the values into form inputs so that they can be sent to the CGI script if desired.

The calculateTax function is defined in the tax-calculations.js file. (The function calculateTax uses the programming concept of return values, which we haven't covered, but don't worry about that.) One nice effect of this technique is that if the tax rate changes (say, the state legislature declares a tax holiday), we can change the definition of the function in that file, and any web page that uses the file will immediately start using the new definition.

Of course, you could get the effect of an external file of JavaScript by including the JavaScript code using SSI, but that requires more processing by the server, so this way is better.

Exercise 1

Download the files for the bill example, and then change the tax rate or change the calculateTax function, say by having it complain if the bill amount is negative. You can do this without knowing about return.

Improved Modularity

First of all, notice that the file of JavaScript could be a collection of random, unrelated, and disorganized code. There's nothing in this mechanism to enforce any kind of conceptual organization to the JavaScript code. You could simply have one file, say mystuff.js, and put anything you want in it.

That would work, but it has the disadvantage that you probably will end up loading code you don't need (a small drag on page loading time). More importantly, it's harder to find the code you want and to keep things organized. Instead, you could divide your code into conceptually coherent groups. For example:

<script type="text/JavaScript" src="date-functions.js"></script>
<script type="text/JavaScript" src="form-validation.js"></script>
<script type="text/JavaScript" src="rollovers.js"></script>
<script type="text/JavaScript" src="lightbox.js"></script>

These files are more worthy of the term module. If someone asks to use your lightbox code, you can point them to that file, and they won't have to wade through irrelevant stuff to use your code.

(Unfortunately, dividing your code into several small files increases the loading time of your website, since the browser needs to make a separate connection for each one. Therefore, professional web designers use tools to combine a bunch of modules into one file, thereby getting the best of both worlds.)

Protecting Your Implementation

Suppose you're the one who implemented the tax-calculations.js module above, and other web designers are using your code on their websites. You know that the variable taxRate is the rate that we pay, and so can anyone reading your code. However, you worry that someone might mistakenly or maliciously set that variable to the wrong value. They could set it to 0.50 instead of 0.05, or even set it to a non-numerical value.

One thing you could do is provide a programmatic way to set the tax rate, rather than just assigning to the variable. For example, something like this:

function setTaxRate (newRate) {
    if( isNaN(newRate) ) {
        alert("Error: the new value is not a number: "+newRate);
    } else if( newRate < 0 ) {
        alert("Negative Tax Rates don't make sense");
    } else if( newRate > 0.3 ) {
        alert("This tax rate is higher than any legal rate "+newRate);
    } else {
        taxRate = newRate;
    }
}

Then, people set the tax rate using this function, instead of just assigning to the variable taxRate. Of course, this function can't check for every possible error, but it could be a help.

But, you object, a malicious person could still set the taxRate variable directly, instead of using this function. That's true. However, there are programming techniques (which we won't go into) that protect values absolutely, so that you must go through a setter function like this in order to set the value. Thus, our module can build up walls around itself, controlling how it is used.

Computer scientists endeavor to create programming languages that are powerful, easy to use, and also encourage and enforce useful modularity. This makes programming better and more effective.

Modularizing Headers and Footers

Suppose we have three pages, A, B, and C, all of which should share a common header (with banner image and navbar) and common footer.

We would like to be able to describe these pages in such a way that we generate the shared parts so that they are easy to change. For example, to add a new menu item or footer item, we should only have to change one piece of code, not code on each of the pages.

Modularity via Server-Side Includes

We have already studied one way to achieve this sort of modularity: server-side includes (SSI). You might want to refresh your memory by reviewing the SSI notes.

Modularity via JavaScript Functions

Another way to achieve the modularity we desire in the A, B, C example is to use JavaScript functions to generate the shared HTML code in each file.

As shown below in the HTML source code for A.html, we can accomplish this using the JavaScript functions addHeader() and addFooter(). (The code for B.html and C.html is exactly the same except for a different content paragraph.)



The file header-footer.js contains definitions of addHeader() and addFooter(), which are responsible for generating the HTML elements that are shared between all the pages.



Any change to the functions addHeader(), addBannerPic(), addNavbar(), addMenuItem(), and addFooter() will affect all of the web pages in the website. For instance, it's easy to add a new menu item to the menu bar or a new icon to the footer list.

See the header-footer example in action