reptile7's JavaScript blog
Sunday, October 24, 2010
 
More on Query String Migration
Blog Entry #194

We return now to our discussion of the "Limitations" section of HTML Goodies' "A Quick Tutorial on JavaScript Variable Passing".

More than two name/value pairs

Let's add some more controls to the jspass1.html form, shall we? Between the FirstName/LastName text inputs and the submit button, let's insert a selection list and a set of radio buttons:

In what month were you born?

In what month were you born?
<select name="Month">
<option>January</option>
<option>February</option>
<!-- Etc. -->


At a Chinese restaurant, you are most likely to order:
Beef with Broccoli
Sweet and Sour Pork
General Tso's Chicken
Shrimp with Garlic Sauce

At a Chinese restaurant, you are most likely to order:<br>
<input type="radio" name="Entree" value="Beef with Broccoli"> Beef with Broccoli<br>
<input type="radio" name="Entree" value="Sweet and Sour Pork"> Sweet and Sour Pork<br>
<!-- Etc. -->


Next, let's fill out and submit the form.
(1-2) Suppose we respectively enter Joe and Burns into the FirstName and LastName fields as we did in the previous post.
(3) We select the October option of the Month menu.
(4) We choose the Beef with Broccoli Chinese Entree.
(5) Finally, we click the button to go to the jspass2.html page.

At the jspass2.html page, here's the query string that we see at the end of the URL in the browser window's address bar:

?FirstName=Joe&LastName=Burns&Month=October&Entree=Beef+with+Broccoli

(The value attribute of the option element defaults to element content if it is not specified.)

Now, how do we extract the user's jspass1.html inputs out of this thing? Joe's delineate( ) function will return the Joe value if we switch its str.lastIndexOf("&") operation to a str.indexOf("&") operation; verbatim, Joe's delineate2( ) function will return the final Beef+with+Broccoli value. But what about the Burns and October values, huh?

Split it

The final paragraph of the "Limitations" section reads in part:
I saw a suggestion on doing this by setting the answers to an array. The array method allows for endless variables being passed, but older browsers would not be able to use it properly. My version allows for the most number of browsers to be able to play with the code but only uses two variables. You decide.
In previous editions of the tutorial - as late as 08 June 2008 - the words "answers to an array" in the above quote linked to a "Passing Data Between Pages via URLs" article authored by JavaScript guru Danny Goodman. The article's "Listing 1" details the "array method" that Joe is talking about; in particular, here's the part of the Listing 1 code that we're interested in (I've modified it slightly):
var results = new Array( );
if (location.search) {
	// Unescape and strip away leading question mark.
	var input = unescape(location.search.substring(1));
	input = input.replace(/\+/g, " ");
	// Divide long string into array of name/value pairs.
	var srchArray = input.split("&");
	var tempArray = new Array( );
	for (i = 0; i < srchArray.length; i++) {
		// Divide each name/value pair temporarily into a two-entry array.
		tempArray = srchArray[i].split("=");
		// Use temp array values as index identifier and value.
		results[tempArray[0]] = tempArray[1]; } }
/* These statements are functionized in Listing 1 but they can also be run as top-level code without incident. */
• In Listing 1, the preceding code is prefaced by some browser-sniffing statements that are no longer relevant in this day and age; the statements were put there to weed out users without support for the split( ) method of the String object, which was implemented in Netscape 3 and MSIE 4.

The search property of the Location object returns the query string portion of the current URL, including the question mark, which is removed for the input string by the location.search.substring(1) operation.

• We briefly discussed the unescape( ) function and its deprecated status in the previous post. I've kept the unescape( ) line because
(a) had we chosen General Tso's Chicken for our Entree, we'd need it to unescape the %27 to which the apostrophe would be percent-encoded, and
(b) modern browsers all support the unescape( ) function - its use throws no errors or warnings as of this writing.
(But if you insist on alternatively working with the encodeURI( )/encodeURIComponent( )/decodeURI( )/decodeURIComponent( ) functions and a homemade query string, then more power to you.)

• The input.replace(/\+/g, " ") operation replaces Beef+with+Broccoli's plus signs with spaces as described in the previous post.

• The above code uses the aforementioned split( ) method to fragment the post-? query string into its constituent parts*, which are organized as an associative array having a results identifier, as though we had written:

var results = new Object( );
results["FirstName"] = "Joe";
results["LastName"] = "Burns";
results["Month"] = "October";
results["Entree"] = "Beef with Broccoli";


(*As we saw in the "Extracting the value value, take 2" section of Blog Entry #83, Microsoft uses this approach to retrieve values from a document.cookie string.)

We can use JavaScript's for...in statement to iterate over the results array and print out its data on the jspass2.html page:

for (var item in results) document.write(item + " is: " + results[item] + "<br>");
/* Output:
FirstName is: Joe
LastName is: Burns
Month is: October
Entree is: Beef with Broccoli */


In each for...in iteration, a results index string is assigned to the variable item, i.e., item is set to FirstName in the first iteration, to LastName in the second iteration, etc. Each item index and its corresponding value, results[item], are then written to the page in a normal manner by a document.write( ) command.

Walking our way across the URL

The array approach of the preceding subsection works very nicely with all of the GUI browsers on my computer (I did encounter one glitch in testing it: the replace( ) operation threw an error with Netscape 3.04, which lacks support for regular expressions). Nonetheless, it's not necessary to use arrays to get our hands on all four jspass1.html form values, as Joe's String method approach is also up to the task of doing so.

In the delineate( ) function, Joe uses the indexOf( ) method to delimit the FirstName field's value in str, the page URL string. (OK, he actually uses the lastIndexOf( ) method to nail down the theright index, but as noted earlier, he should use indexOf( ) for this purpose.) We can similarly use the indexOf( ) method to delimit the LastName field's value in our longer-form query string if we set the second, fromIndex indexOf( ) parameter to the index one past that of the first & in str, i.e., if we begin searching for the second = and the second & in str from the L of LastName (the first character of the second name in the query string):

name2char1 = theright + 1;
theleft2 = str.indexOf("=", name2char1) + 1; // Locates the B of Burns in str
theright2 = str.indexOf("&", name2char1); // Locates the second & in str
document.write("<p>LastName is: " + str.substring(theleft2, theright2)); // Writes LastName is: Burns to the page


Analogous commands can be written to extract the Month value (and subsequent values up to the penultimate value if we were dealing with a larger form) from str.

More than two pages

So, if we wanted to take the jspass1.html form data to a third, jspass3.html page (or beyond), how would we do that? In the tutorial's concluding "That's That" section, Joe says:
You can carry the same info across many pages, but they all must be in order. If you take these variables to yet a third page, all this coding must be redone on the new page. The variable does not carry across pages. The text in the URL carries. Once it gets to the new page, it has to be assigned a new variable...each time.
It is of course true that the jspass2.html data-extraction code must also be placed in the jspass3.html source for the jspass3.html page to make use of the jspass1.html form data. It is also true that the various jspass2.html data-extraction variables (text, str, etc.) will not be carried from jspass2.html to jspass3.html - indeed, we wouldn't need to be doing any of this if a given Web page recognized the variables of its referring page.

However, Joe does not discuss how to take the jspass2.html URL query string to a jspass3.html page. Looking at the tutorial's "Placing the Value" section, my guess is that he would equip the name="bull" form with an action="jspass3.html" attribute and then add a submit button to the form, and thus link jspass2.html to jspass3.html as jspass1.html is linked to jspass2.html. This will work, but the aforecited "Passing Data Between Pages via URLs" article (cf. the "The Search String Is Always Passed From Page to Page" subsection of the article's "The Session Preferences Example" section) describes a better approach to query string migration: use a link that appends location.search to the jspass3.html target anchor URL.

<a href="jspass3.html" onclick="location.href='jspass3.html' + location.search; return false;">Link to jspass3.html</a>

Per the onclick event handler, JavaScript-enabled users will be taken to a 'jspass3.html' + location.search page; per the href attribute, users without JavaScript support or who have disabled JavaScript will also be taken to the jspass3.html page but the jspass2.html location.search query string will not come along for the ride.

And why stop at three pages? Suppose that we have a ten-page Web site - jspass1.html, jspass2.html, ... jspass10.html - and we want the jspass1.html form data to be available to all ten pages. On each page we could have a link menu that links and passes the page's query string to any other page of the site via one of the mechanisms above. In using a form and the HTTP get transaction to move a query string around, it might initially seem that we are limited to sending the form's data to the page specified by the form's action attribute, but this is not so; we can easily overwrite the action value, and thus send the data anywhere we like, as follows:
function passValues2(destination) {
	document.forms[i].action = destination;
	document.forms[i].submit( ); }
...
<a href="jspass#.html" onclick="passValues2('jspass#.html'); return false;">Link to jspass#.html</a>
Backing up

Consider a user who begins touring our ten-page site at jspass1.html, fills out the jspass1.html form, submits the form data and goes to jspass5.html, and then returns to jspass1.html via the browser's Back button or History menu/pane as opposed to using jspass5.html's link to jspass1.html. In the second paragraph of the "Limitations" section, Joe warns, Returning and backing up can harm the information being carried, and Joe is correct in this case in the sense that the jspass5.html query string will not be carried back to jspass1.html (jspass1.html didn't have a query string to begin with, after all). But it's not as though the user's data just goes up in smoke, either; back at the jspass1.html page it'll still be there, loaded into the form's controls, from which it can be extracted to do whatever it is that you want to do with it.

The "Passing Data Between Pages via URLs" article sports a Session Preferences example that would be worth dissecting, but I think I've had enough of this topic for the time being.

In the next entry, we'll take stock of where we are in the Beyond HTML : JavaScript sector and then pick another sector tutorial to check over, probably "How to Populate Fields from New Windows Using JavaScript".

reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)