Let's look at the JavaScript that runs our example. In studying mvctable.js, a veteran DHTML coder will find some surprises due to its use of the new DOM.
For the sake of clarity, mvctable.js is divided into four parts:
Listing 2. The JavaScript source for mvctable.js.
/*
mvctable.js v0.3 Monday, May 31, 1999
Mitch Gould - [email protected]
by Mitch Gould, [email protected]
This script demonstrates dynamic documents in Netscape <br />Navigator and Internet Explorer 5 in the following ways:
(a) it exercises W3C's standard Document Object Model API.
(b) it creates, modifies, and destroys a table.
(c) it partially illustrates the concept of model-view-controller.
(To fully illustrate the MVC concept, modifications made to the
data view would need to be propagated back to the data model.)
*/
// A. Data initialization.
var datacount = 0
model = new Array()
// Allocate the first dataset, a set of quotations, from this array.
arrayquotes = new Array(
"Oh dear! I shall be too late.",
"Curiouser and curiouser!",
"Who are -you-?",
"We're all mad here.",
"Twinkle, twinkle, little bat.",
"Off with her head!",
"I make you a present of everything I've said...",
"Once, I was a real Turtle.",
"Sentence firstverdict afterwards.")
// When a link is clicked, the quotations' sources will be revealed
// from this array.
arraysources = new Array(
"White Rabbit",
"Alice",
"Caterpillar",
"Cheshire Cat",
"Dormouse",
"Red Queen",
"Dutchess",
"Mock Turtle",
"Red Queen")
// B. MVC-DOM methods.
// Establish a new data model and assign it to a new view.
function startController() {
// Populate the model with initial data.
datamodel = refreshModel(arrayquotes)
// Get the document body element.
var docbod = getBody()
/*
Create a view in the body of the document and fill it
with the initial data.
*/
createView(docbod, model)
}
// Copy the specified dataset into the model.
function refreshModel(arraycurrent) {
// Populate the model with the current datastore.
for(var i=0; i < arraycurrent.length; i = i + 1)
{
model[i] = arraycurrent[i]
}
return model
}
/*
this could be generalized to provide alternatives to a
a table view, such as a select object, a tree, or even
a textarea. this sample produces a one-column table.
Multi-column tables are more complex.
*/
function createView(bodyelement, themodel) {
table = document.createelement("TABLE")
table.border = 1
table.id = "viewtable"
tablebody = document.createelement("TBODY")
for(var i=0; i < model.length; i++)
{
currentrow = document.createelement("TR")
currentcell = document.createelement("TD")
currentcell.appendchild(document.createtextnode(model[i]))
currentrow.appendchild(currentcell)
tablebody.appendchild(currentrow)
}
table.appendchild(tablebody)
bodyelement.appendchild(table)
return table
}
// refresh the model first, then the view.
function refreshview(dataset) {
// populate the model with new data.
refreshmodel(dataset)
tablebody = document.getelementsbytagname("TBODY").item(0)
var count = 0
replacealltext(tablebody)
}
// One can also destroy HTML objects using the DOM.
function destroyView() {
objecttodestroy = document.getelementbyid("viewtable")
body = getbody()
body.removechild(objecttodestroy)
// now destroy the buttons.
objecttodestroy = document.getelementbyid("whosaid")
body.removechild(objecttodestroy)
objecttodestroy = document.getelementbyid("goaway")
body.removechild(objecttodestroy)
}
// c. dom tree-navigation and utilities.
/*
One must climb the trunk, branches, and twigs to get
(or set!) the fruit. The recursive nature of this
algorithm reflects an essential fractal nature of
documents.
*/
function replaceAllText(startelem) {
// Climb the object tree, replacing its text nodes with
// new data.
for (var i=0; i < startelem.childnodes.length; i = i + 1) {
switch (startelem.childnodes.item(i).nodetype) {
case 1: // element nodetype
replacealltext(startelem.childnodes.item(i))
break;
case 3: // text nodetype
if (datacount < model.length) {
settext(startelem.childnodes.item(i), model[datacount])
datacount = datacount + 1
} else {
settext(startelem.childnodes.item(i)," - ? - ")
}
break;
} //endswitch
} //endfor
} //endfunction
/*
many operations on dynamic documents require one to start
from the document's body element.
*/
function getBody()
{
if(navigator.appName != "Netscape") {
resultelement = document.body;
} else {
resultelement = document.getelementsbytagname("body").item(0);
}
return resultelement;
}
// utility function to overwrite text nodes.
function settext(tagtoset, valuetoset) {
tagtoset.nodevalue = valuetoset
}
// d. this can't start until the page loads.
window.onload = startcontroller
Section A is simply expected in a script of any size. Here, it sets up the string arrays that contain the Alice quotations and their sources.
The code in the B section provides a way to implement a model-view-controller design pattern using the Document Object Model. This section acts as the controller, initializing the model and view, refreshing the model and view with updated values, and destroying the view upon request.
Section C provides support routines that encapsulate lower-level details of access to document objects. It's a bit disappointing that the DOM doesn't provide standardized access to the document at this level of encapsulation, but once these utility methods are written, they can be reused endlessly. These methods are responsible for navigating the document tree to touch the desired objects, notably in this case, text content that must be replaced.
The D section is required to start the controller as soon as the browser finishes loading the page.
With this background, we're ready to examine the code example. As the page is loading, the browser processes the data initialization section, executing the constructor
model = new Array()
to create an empty model, and creating two more arrays: arrayquotes
and arraysources
.
Initializing the controller
Once the page has finished loading, the controller starts.
// This can't start until the page loads entirely. window.onload = startController
The controller transfers the quotes to the model, and creates a new table in the document to act as a view, as shown in Listing 3.
// B. MVC-DOM methods. // Establish a new data model and assign it to a new view. function startController() { // Populate the model with initial data. datamodel = refreshModel(arrayquotes) // Get the document body element. var docbod = getBody() /* Create a view in the body of the document and fill it with the initial data. */ createView(docbod, model) } |
Listing 3. The startController()
function.
To understand the functions called by startController()
, we must turn to a description of the DOM, which we'll cover in the next section.
JavaScript for the MVC example
Understanding the Document Object Model