Fun With JavaScript: A Simple Test / Quiz Script To Demonstrate DOM Form Handling

Recently asked on Yahoo! Answers:

How can I find or create a Javascript app to make an interactive online test for my students?

I want to make an interactive online test for my students at http://myenglishclass.getpowers.com… I was thinking a javascript file might be perfect, but I don’t know how to program in Java. Is there a file on the net or can someone tell me how to make one?

Making a test or quiz in JavaScript doesn’t make much sense, because you give the answers out in the code, which means all someone has to do is look at the source code of your JavaScript and he’ll have all the right answers.

True, you could obfuscate your code in an attempt to make cheating more difficult, but that requires a fairly advanced knowledge of JavaScript; at the very least, it requires the people you are testing to know less about JavaScript than you do, and that’s unlikely in a group of 30 or so teen-agers.

Nonetheless, while it’s impractical to test kids with JavaScript, it does allow us to examine how to work with form inputs via the HTML DOM (Document Object Model) and JavaScript, so let’s play!

To start, we need a form. It will contain a text box, two radio button lists, a check box list, and a pull-down (select) menu. We’re using all these different elements because the way you handle each is different.

<form id="test">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<td>Who was the first president of the United States?</td>
<td>
<input id="q1" maxlength="30" name="q1" size="30" type="text" /></td>
</tr>
<tr>
<td>There are 13 items in a baker's dozen.</td>
<td>
<input name="q2" type="radio" value="true" />
        True |
<input name="q2" type="radio" value="false" />
        False</td>
</tr>
<tr>
<td>What is the capital of Maine?</td>
<td>
<input name="q3" type="radio" value="bangor" />
      Bangor |
<input name="q3" type="radio" value="east overshoe" />
      East Overshoe |
<input name="q3" type="radio" value="portland" />
      Portland |
<input name="q3" type="radio" value="augusta" />
      Augusta |
<input name="q3" type="radio" value="none" />
      None of these</td>
</tr>
<tr>
<td width="30%">Which of the following animals are typically found on an American farm? [Check all that apply]</td>
<td>
<input id="q4" name="q4" type="checkbox" value="pig" />
        Pig |
<input id="q4" name="q4" type="checkbox" value="elephant" />
        Elephant |
<input id="q4" name="q4" type="checkbox" value="cow" />
        Cow |
<input id="q4" name="q4" type="checkbox" value="kangaroo" />
        Kangaroo</td>
</tr>
<tr>
<td>The Nile River's estuary is located in what nation?</td>
<td>
<select id="q5" name="q5">
        <option value="saudi arabia">Saudi Arabia</option>
        <option value="england">England</option>
        <option value="libya">Libya</option>
        <option value="egypt">Egypt</option>
        <option value="israel">Israel</option>
      </select></td>
</tr>
</table>
<input onclick="gradeTest()" name="submit" type="button" value="Grade This Test" />
</form>

Notice that some of our form elements have an id attribute, some have a name attribute, and some have both. The reason for that will be explained shortly.

Note also that the submit button for this form has been assigned the type “button.” Since we’re going to handle the form’s values entirely on the client side, we don’t need to post back to the server; the “submit” type would post the information back.

We do, however, assign a JavaScript function named gradeTest() to the onclick event for the button. Let’s get started with that function by declaring some variables:

  • totalQuestions is the number of questions in the quiz
  • correctAnswers is the current count of answers the student got right
  • alertText is the string we’ll output once we are done grading the quiz
  • i is our all-purpose looping variable
function gradeTest() {
	//variables with global implications
	var totalQuestions = 5;
	var correctAnswers = 0;
	var alertText;
	var i;

We can now handle the text box’s input. This is pretty simple: We’ll cast the input down to lowercase, so we can be sure we don’t get a comparison mismatch due to case, and then compare the value of the textbox to the right answer. If we have the right answer, we increment the correct answer counter by 1.

var a1 = document.getElementById('q1').value.toLowerCase();
	if(a1 == 'george washington') {
		correctAnswers++;
	}

Dealing with radio button lists is a bit more complicated.

You’ll find plenty of examples out there showing you how to work with a single radio button — get its value, see if it is selected, etc. What you won’t find is a lot of information on working with multiple radio buttons or check boxes that have the same name but several different values.

That’s because JavaScript accepts radiobuttonlists and checkboxlists (that is, radio buttons or checkboxes that have multiple options under the same variable name) as arrays. Even if only one item is selected, JavaScript handles the data as an array. Therefore, to process radiobuttonlists or checkboxlists, we have to iterate through them as we would any array.

The way we get all the items in a radiobuttonlist or checkboxlist is via getElementsByName. Like getElementById, getElementsByName will find, within our HTML document, all the items that has the specified name attribute.

In the HTML DOM, each element can have only one ID, but many elements can have the same name. For example, you can have only one element like
<div id=”mydiv”>in a given Web page, but you can have multiple elements like <div>in a single page.

You can also have a <div name=”mydiv”> and a <input id=”myinput” name=”mydiv” type=”text” />; as far as getElementsByName is concerned, both of those elements will be found. So, as far as accessing HTML elements via JavaScript goes, we need to make sure we only assign names to elements that we want joined together in an array.

So getElementsByName returns an array of objects, in the top-down order they are found in the document. In the preceeding case, getElementsByName(‘mydiv’) would return an array containing a div and a textbox. In the case of getting all the named choices in a radiobuttonlist or checkboxlist, getElementsByName will return an array of all the radio buttons or check boxes that have the name you specify.

So, once we get all the radio buttons or checkboxes that have the same name, we can iterate through the array, treating each item in the array as though it were a single radio button or checkbox. We can see if the item is checked, what is value is, etc.

In our quiz script’s radiobuttonlists, if we find the correct answer is checked, then we have a correct answer; we increment the correct answer counter and break out of the for loop that is iterating through the radiobuttonlist, since we’ve found what we are after.

var a2 = document.getElementsByName('q2');
	for(i = 0; i < a2.length; i++) {
		if(a2[i].checked) {
			if(a2[i].value == 'true') {
				correctAnswers++;
				break;
			}
		}
	}

Question 3 is meant to demonstrate that even with many more options in a radiobuttonlist, the basic code to process the answers isn’t any different than Question 2’s code.

var a3 = document.getElementsByName('q3');
	for(i = 0; i < a3.length; i++) {
		if(a3[i].checked) {
			if(a3[i].value == 'augusta') {
				correctAnswers++;
				break;
			}
		}
	}

The way you work with values from a checkboxlist is the same as the way you do it with a radiobuttonlist, save that the user can make multiple checkbox selections. So, we need to collect all the checkboxes that are checked. Fortunately, dealing with multiple answers in a checkboxlist is as simple as comparing arrays.

  • We put the correct answers in an array
  • We construct a second array by pushing onto it all the answers selected by the user
  • We sort both arrays by the same criteria
  • If both arrays have the same length and the same values at the same indexes following a sort, the arrays are the same, so the user has chosen the correct answers; increment the correct answers counter
var a4 = document.getElementsByName('q4');
	var a4answers = new Array();
	var a4right = new Array('pig','cow');
	var a4bool = true;

	for(i = 0; i < a4.length; i++) {
		if(a4[i].checked) {
			a4answers.push(a4[i].value);
		}
	}
	a4answers.sort();
	a4right.sort();
	if(a4answers.length == a4right.length) {
		for(i = 0; i < a4answers.length; i++) {
			if(a4answers[i] != a4right[i]) {
				a4bool = false;
				break;
			}
		}
	}
	else {
		a4bool = false;
	}
	if(a4bool == true) {
		correctAnswers++;
	}

Dealing with select boxes (drop-down / pull-down lists) is the simplest task, thanks to the selectedIndex property. All we need to know is where in the list the correct answer falls.

For example, if the correct answer is the fourth item in our select list, then if the selectedIndex of the pull-down menu is 3, we have the right answer. [As with arrays, all indexes in JavaScript are numbered from zero.]

var q5 = document.getElementById('q5');
	if(q5.selectedIndex == 3) {
		correctAnswers++;
	}

To finish up our quiz, we construct the string we’ll use in an alert box, then pop that alert box up to the user:

	if(correctAnswers == totalQuestions) {
		alertText = "Congratulations! You got all the questions right!";
	}
	else {
		alertText = "You got " + correctAnswers + " out of " + totalQuestions + " correct!";
	}
	alert(alertText);
}

I’ve tested this script on Firefox 1.5 and Internet Explorer 6, and it works fine in both. You can see it in action on your Web browser at:

http://demo.dougv.com/jsquiz/

I distribute code under the GNU GPL. See Copyright & Attribution for details.

10 Comments

  1. Wow,
    I did not expect such a great answer to my question. I don’t think my students would go through the trouble to look up the answers by getting the source code off the Internet. Its not a test for a real school, its an online study English website.

    Basically, I record my voice and give a listening lesson to my students. What I want to do is give the students a chance to test themselves online, more for fun and practice, not a serious college test or anything like that. Just for fun and practice.

    I will look over this code carefully and see if I can use it on my webpage. Thank you very very much!

    Mark http://www.getpowers.com

  2. Works great, next time I would suggest maybe putting the questions after a button like this: Next question

    $("#button1").click(function(){
    windows.location = "nextpage.html"
    });
    

    Otherwise perfect tnx – Brandon Jakobson

Leave a Reply

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!