[ Team LiB ] Previous Section Next Section

14.5 Embedding Data as JavaScript Objects

NN 4, IE 4

14.5.1 Problem

You want to include data retrieved by server processes in the JavaScript code of the page, so that client scripts can manipulate the data and/or provide rendering options for the user.

14.5.2 Solution

Your server processing code can insert blocks of dynamically assembled data into portions of the HTML page about to be served to the client. While string data is typically sent to the client as values of hidden input elements, JavaScript data structures offer significantly more power and flexibility once the code loads into the client.

For simple collections of related values, JavaScript arrays are ideal mechanisms, especially with the shortcut syntax that simplifies code assembly on the server:

var dataArray = ["value0", "value1", "value2", ... "valueN"];

For repetitive database-record-like data, shortcut JavaScript object creation syntax makes it a snap to define properties and their values:

var dataObject = {prop0:"value0", prop1:"value1", prop2:"value2", ... 
    propN:"valueN"};

To create an array of objects, take advantage of the length of a newly created array to get started. By using a calculated array index value, you can modify the sequence of array entries without having to renumber the indexes manually:

var dataArray = new Array( );
dataArray[dataArray.length] = {prop0_0:"value0_0", prop0_1:"value0_1", 
    prop0_2:"value0_2", ... prop0_N:"value0_N"};
dataArray[dataArray.length] = {prop1_0:"value1_0", prop1_1:"value1_1", 
    prop1_2:"value1_2", ... prop1_N:"value1_N"};
...

Values assignable as array items or object values can be quoted strings (shown here), numbers, Booleans, and other arrays and objects. In fact, there is no practical limit to the amount of nesting you can do to create complex objects whose properties are, themselves, complex objects.

14.5.3 Discussion

If you define the JavaScript data objects in a script within the <head> tag, those objects are defined and ready to go when the body starts rendering. Therefore, you have the choice of utilizing those objects to generate dynamic HTML while the page loads, or of using the body's onload event handler to trigger a function that modifies the body delivered with the page.

You don't have to embed the data formally within the HTML page. Instead, a separate server process (invokeable via URL) can serve as the source for the equivalent of a .js script library. Specify the URL of the process as the value of the <script> tag's src attribute. Make sure that the string content returned from the process is in the same script-only form as any valid .js file and that the content type for the output is text/javascript. Because of potential synchronization problems with a secondary server request, access the data delivered this way via the page's onload event handler only.

One of the most convenient JavaScript data formats is an object utilizing string index values. These aren't arrays per se, since the object does not gain a length property, and iterating through its members requires the for-in style of object introspection. A little known fact is that JavaScript lets you build this pseudohash table on top of an array object, and the two sets of data do not collide (as shown in Recipe 3.9). For example, consider the following JavaScript data variation of the World Cup final match records, delivered initially as an array of objects:

var matchData = new Array( );
matchData[0] = {loser:"Argentina", losscore:"2", location:"Uruguay", 
winner:"Uruguay", winscore:"4", year:"1930"};
matchData[1] = {loser:"Czechoslovakia", losscore:"1", location:"Italy", 
winner:"Italy", winscore:"2", year:"1934"};
...

Now imagine a page that has a select element containing a list of the years of all the matches, allowing a user to select a year to display the details of the final match. The slow way to reach the data is to use a for loop through the matchData array, looking at each year property value in search of a match of the chosen option value. For a sizable array, this could take a few seconds. But a one-time preprocessing of the array can create the pseudohash table with the years as string index values. Immediately after the matchData array is completely populated, the following statements generate the hash table:

for (var i = 0; i < matchData.length; i++) {
    matchData["_" + matchData[i].year] = matchData[i];
}

Notice an interesting twist that the sample data required: because the year property values being used as hash table indexes automatically cast to numeric values when placed in that context, a nonnumeric value (an underscore) is concatenated onto each value to force the index to be a string value. Otherwise, the numeric value increases the length of the array, without generating the string-indexed hash table. For nonnumeric property values, you can eliminate the concatenation trick and supply just the property reference.

Once the hash table is created, you can now reference the array object through the unique string index of the hash table, as in this oversimplified example:

<select onchange="alert('Winner was: ' + matchData[this.value].winner)">
<option value="_1930">1930</option>
<option value="_1934">1934</option>
...
</select>

14.5.4 See Also

Recipe 3.1 for creating arrays; Recipe 3.8 for creating custom objects; Recipe 3.9 for generating hash tables from arrays or arrays of objects; Recipe 3.11 for sorting arrays of objects.

    [ Team LiB ] Previous Section Next Section