Modularity is something that has come up several times in this course, in different guises. We'll discuss this important principle both in general and how it specifically might apply to the web sites we'll build.
In this reading, 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
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 ...
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.
As we saw when we introduced JavaScript, 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.
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. You don't have to read every line, but the first three functions are important (lines 20-45). The rest of the code defines function to add clickable items to the page.
Some key things to look at and think about are:
Another example is some JavaScript that processes a form. Here's an example:
The HTML for the form is straightforward. We'll learn about this a bit
later in the course, so don't worry about the details. The main point
is that we can get the amount of the bill from the user, rather than
usingprompt()
.
The JavaScript code associated with this form is the following, which
uses the jQuery .val()
method to get the value that the
user filled into the form. Again, we'll cover this a bit later, so
don't worry about the details.
(The change()
method attaches a handler that is
invoked whenever the value in the form changes. We'll learn about event
handlers very soon.)
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
input, also has outputs 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. 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.
If we create a file of JavaScript code for reuse in other web pages, it
could just be a collection of random, unrelated, and disorganized
functions. 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.)
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. If the true tax rate is
5 percent, they could set the variable to 5 (which would be a 500 percent
rate) or accidentally set it to 0.50 instead of 0.05 (just a typo), or
even set it to a non-numerical value.
To avoid these mistakes, 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; // finally, set the global variable. } }
Then, people set the tax rate using this function, instead of just
assigning to the variable taxRate
. Of course,
our setTaxRate
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.
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've already studied one way that this common markup can be shared
among the pages, namely using the jQuery .load()
function
to load the code from a master copy, probably on the main page. In the
following sections, we'll describe a second approach.
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.
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-jq.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