Dynamic Forms
- To create jump menus.
- To create interdependent select menus.
- To create a JavaScript TIMER
- To build a simple JavaScript testing tool.
Jump Menus
A jump menu is a select menu that contains a list of websites or pages to visit. There are two main types of jump menus. One jumps to the selected page as soon as the user makes a selection. The other jumps to a page only after the user has made a selection and clicked on a "Go" button. We'll look at the latter type first and then create the former type in an exercise.
Code Sample: DynamicForms/Demos/JumpMenus.html
<html>
<head>
<title>Jump Menus</title>
<script type="text/javascript">
function jumpMenu(select){
var i = select.selectedIndex;
var selection = select.options[i].value;
var url;
if (i === 0) {
alert("Please select a state.");
} else {
url = "http://www.50states.com/" + selection + ".htm";
location.href = url;
}
}
</script>
</head>
<body>
<form>
<select name="State">
<option value="0">--SELECT A STATE--</option>
<option value="alabama">Alabama</option>
<option value="illinois">Illinois</option>
<option value="massachu">Massachusetts</option>
<option value="montana">Montana</option>
<option value="ncarolin">North Carolina</option>
<option value="wvirgini">West Virginia</option>
</select>
<input type="button" value="GO"
onclick="jumpMenu(this.form.State);">
</form>
</body>
</html>
Viewed in a browser, the page looks like this:
![]()
The options[] Array
Select menus contain an array of options. Like with all JavaScript arrays, the first item has an index of 0. So, in this example, the first option, which reads "--SELECT A STATE--" is option 0.
The selectedIndex Property
Select menus have a selectedIndex property that holds the index of the currently selected option. For example, in the sample above, if North Carolina is selected, State.selectedIndex would hold 5. To see if the first option of a select menu is selected, check if selectedIndex is 0.
Let's look at the code above in detail.
- The "GO" button has an onclick event handler that, when clicked, passes the State select menu to the jumpMenu() function.
<input type="button" value="GO" onclick="jumpMenu(this.form.State);">
- The jumpMenu() function does three things:
- Creates the variable i that holds the selectedIndex property of the passed-in select menu.
- Creates the variable selection that holds the value of the selected option.
- Checks to see if the first option is selected.
- If it is, the function alerts the user to select an option.
- If it is not, the function creates a variable, url, to hold the destination page and then loads that page by changing the href property of the location object to url.
Disabling Form Elements
Form elements can be disabled by setting the element's disabled property to true. They can be re-enabled by setting the disabled property to false.
FormElement.disabled = true; FormElement.disabled = false;
The example below is a modified version of the jump menu that disables the "GO" button unless a state is selected.
Code Sample: DynamicForms/Demos/JumpMenus2.html
<html>
<head>
<title>Jump Menus</title>
<script type="text/javascript">
function jumpMenu(select){
var i = select.selectedIndex;
var selection = select.options[i].value;
var url = "http://www.50states.com/" + selection + ".htm";
location.href = url;
}
function toggleButton(form){
if (form.State.selectedIndex === 0) {
form.btnJump.disabled = true;
} else {
form.btnJump.disabled = false;
}
}
</script>
</head>
<body onload="ToggleButton(document.forms[0]);">
<form>
<select name="State" onChange="toggleButton(this.form);">
<option value="0">--SELECT A STATE--</option>
<option value="alabama">Alabama</option>
<option value="illinois">Illinois</option>
<option value="massachu">Massachusetts</option>
<option value="montana">Montana</option>
<option value="ncarolin">North Carolina</option>
<option value="wvirgini">West Virginia</option>
</select>
<input type="button" name="btnJump" value="GO"
onclick="jumpMenu(this.form.State);">
</form>
</body>
</html>
Notice that the jumpMenu() function no longer checks if a state has been selected. It is only called when the user clicks the "Go" button, which is only enabled if a state is selected.
Interdependent Select Menus
Interdependent select menus allow you to populate one select menu based on a choice made in another select menu. For example, a menu of cities might change based on the state that was selected in another menu, as shown in the following example.
Code Sample: DynamicForms/Demos/Interdependent.html
<html>
<head>
<title>Interdependent Select Menus</title>
<script type="text/javascript">
var NEW_YORKERS = [];
var CALIFORNIANS = [];
NEW_YORKERS[0] = new Option("New York City", "NYC");
NEW_YORKERS[1] = new Option("Syracuse", "SYR");
NEW_YORKERS[2] = new Option("Albany", "ALB");
NEW_YORKERS[3] = new Option("Rochester", "ROC");
CALIFORNIANS[0] = new Option("Los Angeles", "LAN");
CALIFORNIANS[1] = new Option("San Diego", "SDI");
CALIFORNIANS[2] = new Option("San Francisco", "SFR");
CALIFORNIANS[3] = new Option("Oakland", "OAK");
function populateSub(mainSel, subSel){
var mainMenu = mainSel;
var subMenu = subSel;
var subMenuItems;
subMenu.options.length = 0;
switch (mainMenu.selectedIndex) {
case 0:
subMenuItems = NEW_YORKERS;
break;
case 1:
subMenuItems = CALIFORNIANS;
break;
}
for (var i = 0; i < subMenuItems.length; i++) {
subMenu.options[i] = subMenuItems[i];
}
}
</script>
</head>
<body>
<form name="Menus">
<select name="State" onchange="populateSub(this, this.form.City);">
<option value="NY">New York</option>
<option value="CA">California</option>
</select>
<select name="City">
<option value="NYC">New York City</option>
<option value="SYR">Syracuse</option>
<option value="ALB">Albany</option>
<option value="ROC">Rochester</option>
</select>
</form>
</body>
</html>
Let's look at the code above in detail.
- As the page loads, two arrays (NEW_YORKERS and CALIFORNIANS) are created and then populated with Options using the Option() constructor. The Option() constructor takes four parameters: text, value, defaultSelected, and selected. Only text is required.
- The body of the page contains a form with two select menus: State and City. When a state is selected, the onchange event handler triggers a call to the populateSub() function, passing in the State menu as mainSel and the City menu as subSel.
- A few variables are created and then the City menu is emptied with the following code.
subMenu.options.length = 0;- A switch statement based on the option selected in the main menu is used to determine the array to look in for the submenu's options.
switch (mainMenu.selectedIndex) { case 0: arrSubMenu = NEW_YORKERS; break; case 1: arrSubMenu = CALIFORNIANS; break; }- A for loop is used to loop through the array populating the submenu with options.
Making the Code Modular
A problem with the code in DynamicForms/Demos/Interdependent.html is that it would need to be modified in several places to add or change options. This next example, though more complex, is much more modular, and hence, reusable.
Code Sample: DynamicForms/Demos/Interdependent2.html
<html>
<head>
<title>Interdependent Select Menus</title>
<script type="text/javascript">
var MENU = [];
MENU[0] = [];
MENU[1] = [];
MENU[0][0] = new Option("New York", "NY");
MENU[0][1] = new Option("New York City", "NYC");
MENU[0][2] = new Option("Syracuse", "SYR");
MENU[0][3] = new Option("Albany", "ALB");
MENU[0][4] = new Option("Rochester", "ROC");
MENU[1][0] = new Option("California", "CA");
MENU[1][1] = new Option("Los Angeles", "LAN");
MENU[1][2] = new Option("San Diego", "SDI");
MENU[1][3] = new Option("San Francisco", "SFR");
MENU[1][4] = new Option("Oakland", "OAK");
function populateMain(mainSel, subSel){
var mainMenu = mainSel;
var subMenu = subSel;
mainMenu.options.length = 0;
for (var i = 0; i < MENU.length; i++) {
mainMenu.options[i] = MENU[i][0];
}
populateSub(mainMenu, subMenu);
}
function populateSub(mainSel, subSel){
var mainMenu = mainSel;
var subMenu = subSel;
var optMainMenu;
subMenu.options.length = 1;
optMainMenu = mainMenu.selectedIndex;
for (var i = 1; i < MENU[optMainMenu].length; i++) {
subMenu.options[i] = MENU[optMainMenu][i];
}
}
</script>
</head>
<body onload="populateMain(document.Menus.State, document.Menus.City);">
<form name="Menus">
<select name="State" onchange="populateSub(this, this.form.City);"></select>
<select name="City">
<option value="0">--Please Choose--</option>
</select>
</form>
</body>
</html>
This example uses a two-dimensional array to hold the menus. The first item of each array holds the State options, which is used in the main menu. The rest of the items in each array hold the City options used to populate the submenu.
The State select menu starts out empty and the City menu starts out with just a single "Please Choose" option. The two functions populateMain() and populateSub() are used to populate the two menus. Both functions are completely generic and reusable.
Creating a JavaScript Timer
JavaScript timers can be used to create timed quizzes or events. The trick to a timer is to call a function recursively on a time increment.
The setTimeout() Method
The window object's setTimeout() method is used to execute a block of JavaScript code every n milliseconds. The syntax is shown below.
setTimeout("statements to execute", n);Or
setTimeout(functionToCall, n);
The example below shows how the setTimeout() method can be used to create a timer.
Code Sample: DynamicForms/Demos/Timer.html
<html>
<head>
<title>JavaScript Timer</title>
<script type="text/javascript">
var SECONDS_LEFT, TIMER, TIMES_UP;
function init(){
document.Timer.btnStart.disabled = true;
}
function resetTimer(seconds){
SECONDS_LEFT = seconds;
document.Timer.TimeLeft.value = SECONDS_LEFT;
clearTimeout(TIMER);
document.Timer.btnStart.disabled = false;
document.Timer.btnReset.disabled = true;
}
function decrementTimer(){
TIMES_UP = false;
document.Timer.TimeLeft.value = SECONDS_LEFT;
document.Timer.btnStart.disabled = true;
document.Timer.btnReset.disabled = false;
SECONDS_LEFT--;
if (SECONDS_LEFT >= 0) {
TIMER = setTimeout(decrementTimer, 1000);
} else {
alert("Time's up!");
resetTimer(10);
}
}
</script>
</head>
<body onload="init();">
<form name="Timer" onsubmit="return false;">
Timer: <input type="text" name="TimeLeft" size="2"
style="text-align:center" onfocus="this.blur();">
seconds left<br>
<input type="button" name="btnStart"
value="Start" onclick="decrementTimer();">
<input type="button" name="btnReset"
value="Reset" onclick="resetTimer(10);">
</form>
</body>
</html>
Let's look at the code above in detail.
- As the page loads, three global variables are created: SECONDS_LEFT to hold the number of seconds left, TIMER - to hold the timer, and TIMES_UP - to flag if the timer has run out.
var SECONDS_LEFT, TIMER, TIMES_UP;- The body of the page contains a form with a text field holding the number of seconds left, a "Start" button and a "Reset" button.
<form name="Timer" onsubmit="return false;"> Timer: <input type="text" name="TimeLeft" size="2" style="text-aliagn:center" onfocus="this.blur();"> seconds left<br> <input type="button" name="btnStart" value="Start" onclick="decrementTimer();"> <input type="button" name="btnReset" value="Reset" onclick="resetTimer(10);"> </form>- The onload event handler of the body tag triggers the init() function, which disables the "Start" button.
function init(){ document.Timer.btnStart.disabled = true; }- When the user clicks on the "Reset" button, the resetTimer() function is called. This function does the following:
- Sets SECONDS_LEFT to the number passed into the function.
SECONDS_LEFT = seconds;- Sets the value of the TimeLeft text field to SECONDS_LEFT.
document.Timer.TimeLeft.value = SECONDS_LEFT;- Clears the TIMER timer.
clearTimeout(TIMER);- Enables the "Start" button.
document.Timer.btnStart.disabled = false;- Disables the "Reset" button.
document.Timer.btnReset.disabled = true;- When the user clicks on the "Start" button, the decrementTimer() function is called. This function does the following:
- Sets TIMES_UP to false.
TIMES_UP = false;- Sets the value of the TimeLeft text field to SECONDS_LEFT.
document.Timer.TimeLeft.value = SECONDS_LEFT;- Disables the "Start" button.
document.Timer.btnStart.disabled = true;- Enables the "Reset" button.
document.Timer.btnReset.disabled = false;- Decrements SECONDS_LEFT by one.
SECONDS_LEFT--;- Checks to see if SECONDS_LEFT is greater than or equal to zero.
if (SECONDS_LEFT >= 0) { TIMER = setTimeout(decrementTimer, 1000); } else { alert("Times up!"); resetTimer(10); }
- If SECONDS_LEFT is greater than or equal to zero, setTimeout() is used to re-call decrementTimer() after 1000 milliseconds (1 second). This creates a timer object, which is assigned to TIMER.
- If SECONDS_LEFT is less than zero, an alert pops up notifying the user that time is up and resetTimer(), which clears the timer, is called.
A Sample Quiz Tool
The following example brings together the concepts learned in this lesson to create a quiz tool. The quiz looks like this:
Code Sample: DynamicForms/Demos/MathQuiz.html
<html>
<head>
<title>Math Quiz</title>
<script type="text/javascript" src="Select.js"></script>
<script type="text/javascript">
var MENU = [];
var SECONDS_LEFT, TIMER, TIMES_UP, TOTAL_TIME = 10;
function init(){
MENU[0] = [];
MENU[1] = [];
MENU[2] = [];
MENU[3] = [];
MENU[0][0] = new Option("Addition");
MENU[0][1] = new Option("5 + 4", "9");
MENU[0][2] = new Option("9 + 3", "12");
MENU[0][3] = new Option("7 + 12", "19");
MENU[0][4] = new Option("13 + 24", "37");
MENU[1][0] = new Option("Subtraction");
MENU[1][1] = new Option("5 - 1", "4");
MENU[1][2] = new Option("12 - 4", "8");
MENU[1][3] = new Option("23 - 11", "12");
MENU[1][4] = new Option("57 - 19", "38");
MENU[2][0] = new Option("Multiplication");
MENU[2][1] = new Option("1 * 3", "3");
MENU[2][2] = new Option("4 * 8", "32");
MENU[2][3] = new Option("7 * 12", "84");
MENU[2][4] = new Option("13 * 17", "221");
MENU[3][0] = new Option("Division");
MENU[3][1] = new Option("4 / 1", "4");
MENU[3][2] = new Option("12 / 3", "4");
MENU[3][3] = new Option("21 / 7", "3");
MENU[3][4] = new Option("121 / 11", "11");
populateMain(document.Quiz.Operator, document.Quiz.Question);
resetTimer(TOTAL_TIME);
document.Quiz.btnCheck.disabled = true;
document.Quiz.Answer.disabled = true;
}
function resetTimer(seconds){
TIMES_UP = true;
SECONDS_LEFT = seconds;
document.Quiz.TimeLeft.value = SECONDS_LEFT;
clearTimeout(TIMER);
document.Quiz.Answer.value = "";
}
function decrementTimer(){
TIMES_UP = false;
document.Quiz.TimeLeft.value = SECONDS_LEFT;
SECONDS_LEFT--;
if (SECONDS_LEFT >= 0) {
TIMER = setTimeout(decrementTimer, 1000);
} else {
alert("Time's up! The answer is " + getAnswer() + ".");
resetTimer(TOTAL_TIME);
}
}
function checkAnswer(answer){
var correctAnswer = getAnswer();
if (answer === correctAnswer) {
alert("Right! The answer is " + correctAnswer + ".");
} else {
alert("Sorry. The correct answer is " + correctAnswer + ".");
}
removeOption();
questionChange();
}
function removeOption(){
var i = document.Quiz.Operator.selectedIndex;
var j = document.Quiz.Question.selectedIndex;
MENU[i].splice(j, 1);
if (MENU[i].length == 1) {
MENU.splice(i, 1);
if (MENU.length === 0) {
endQuiz();
}
}
populateMain(document.Quiz.Operator, document.Quiz.Question);
resetTimer(TOTAL_TIME);
}
function questionChange(){
if (document.Quiz.Question.selectedIndex === 0) {
document.Quiz.btnCheck.disabled = true;
document.Quiz.Answer.disabled = true;
resetTimer(TOTAL_TIME);
} else {
document.Quiz.btnCheck.disabled = false;
document.Quiz.Answer.disabled = false;
decrementTimer();
}
}
function endQuiz(){
resetTimer(TOTAL_TIME);
alert("Thanks for playing! The quiz will now reload.");
init();
}
function getAnswer(){
var i = document.Quiz.Question.selectedIndex;
var answer = document.Quiz.Question[i].value;
return answer;
}
</script>
</head>
<body onload="init();">
<form name="Quiz" onsubmit="return false;">
<table border="1" cellspacing="0" cellpadding="4" align="center">
<tr>
<td>Category:</td>
<td>
<select name="Operator"
onchange="populateSub(this, this.form.Question);
resetTimer(TOTAL_TIME);">
</select>
</td>
</tr>
<tr>
<td>Question:</td>
<td>
<select name="Question"
onchange="questionChange();">
<option value="0">--Please Choose--</option>
</select>
</td>
</tr>
<tr>
<td>Answer:</td>
<td>
<input type="text" name="Answer" size="2">
<input type="button" name="btnCheck" value="Check Answer"
onclick="checkAnswer(this.form.Answer.value);">
</td>
</tr>
<tr>
<td>Timer:</td>
<td><input type="text" name="TimeLeft" size="2"
style="text-align:center" onfocus="this.blur();">
seconds left</td>
</tr>
</table>
</form>
</body>
</html>
Here's how the quiz works:
- The Question select menu is always populated with questions in the indicated category.
- The "Check Answer" button and the Answer text field are only enabled when a question is selected.
- The timer starts when a question is selected and stops when it runs out or when the "Check Answer" button is clicked.
- When the "Check Answer" button is clicked, the user is alerted as to whether or not the answer is correct and the question is removed from the question menu.
- When all questions in a category are gone, the category is removed from the category menu.
- When all questions in all categories have been completed, the user is thanked for taking the quiz and the entire quiz is restarted.
Spend some time reviewing this code. You shouldn't see anything new, except in the way the code is designed.
Dynamic Forms Conclusion
In this lesson of the JavaScript tutorial, you have learned to build interactive, dynamic forms.
