Microsoft Hires Another Yahoo For Its Search Team
Posted using ShareThis
Follow the creation and evolution of a JavaScript-based quiz application as a developer satisfies the specified requirements for a client-side-only solution.
App building
In this article, we are going to look at the creation and evolution of a JavaScript-based quiz application. I created the program for a client of mine who had some very specific and also some very vague key requirements. The key requirements included the following:
The client wanted the program to be available on their Web site and give them the flexibility to add multiple sets of questions to the same generic application. In some cases the results of the quizzes needed to be sent to a manager or assessor for review.
Laying the foundations
To begin with I added several utility JavaScript functions from previous projects that I would need to use in this one to provide some of the functionality. This reuse of existing code speeds up the development time as these function are known to be working and thus require minimum rework:
For the quiz itself, the requirements were to allow several question types to give the person creating the quiz the maximum possible choice. The types of questions that were decided upon are as follows:
It was felt that these five question types would provide enough range for the majority of questions that could be included in a quiz.
Building the Quiz
For a quiz program, there are two main phases of work for it to perform. The first is the displaying of the questions to the user via the screen and the second is the marking of the quiz by comparing the values entered by the user against the correct answers stored in the program’s data file. We will look at each in turn.
Creating the XML Data source
As mentioned above, the questions and other details will be stored in an XML file. The quiz programs data is contained within the top level node <quiz>. A string to be displayed as the title for the quiz can be entered into the <quiz_title> attribute. The e-mail address to send the results to is held in the <quiz_send_to> attribute.
The next attribute <quiz_show_points> contains a Boolean value to indicate whether or not you want the user to be aware of the possible score for each question in the quiz.
Each Question itself is held in a <quiz_question> node. Within this node the following elements provide the detail on the question:
Element name
Element description
question_ask
Contains the Question the user sees
question_type
Type of Answer, options are all lower case – radio, dropdown, checkbox, text, selectmany
question_answer
In the case of Radio / Dropdown questions, this is the ID of the correct answer. For CHECKBOX / SELECT-MANY this is the Boolean pattern that the selected checkboxes must match – e.g. 011 means only the last two (of three) must be checked, for TEXT, this is the complete text of the answer.
question_points
The number of points for this questionââ,¬"can be shown to the user if the is true, otherwise just used for scoring the quiz.
Within the quiz_question node, there are also two sub-nodes which contain the list of possible answers and a text message to display to the user for each question when the text is marked. For the former, the node <possible_answers> is used to contain a listing of possible answers to show the user, it is not used for questions of type Text. Within this node, only one element type is allowed, <question_option qno="X">, which containing the text to be shown to the user for each answer, the X is a reference to the answer number.
The second sub-node, <display_answers> holds the text to display to the user when the quiz is marked. Again, this node has only acceptable one child element, <question_option qno="X">, although there should be the same number of occurrences of this node as there are of <question_option qno="X"> elements for the same question. A sample file can be seen in Listing A.
Reading in the XML File
The XML will be read in using a standard XML File reader function, but the processing of the content of the XML file requires a specific function to handle the population of the data structures in the application from the data in the XML file. The initial requirement was that there would be only one supported browser — Internet Explorer — and this code reflects that requirement, although it would not be too difficult to add additional browser support for other browsers such as Firefox, Netscape, Opera etc.
This function shown in Listing B moves through the structured XML file and converts the data into the format that the application requires:
This function shown in Listing B moves through the structured XML file and converts the data into the format that the application requires:
Listing B
var sendTo=""; // e-mail address(es) to send the results to var quizTitle=""; // title of the Quiz to be displayed var questionArray = new Array(); // array of questions var str_xmlFile=""; // URL to the XML file to be used var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); // XML Object – IE only at the moment var xmlObj; // XML Instance var e-mailEnabled=false; // do we send an e-mail / show a HTML results page var show_points; // do we show the points per question // this function loads the Quiz XML File // amended from the code at function loadXML(xmlFile) { // define the variables to store the data from the XML File var question=""; var questionType=""; var questionAnswer=""; var points=0; var possibleAnswers = new Array(); var displayAnswers = new Array(); var optionListLength; // load the XML file xmlDoc.async="false"; xmlDoc.onreadystatechange=verify; xmlDoc.load(xmlFile); xmlObj=xmlDoc.documentElement; // process each element in the XML file for (i=0;i<xmlObj.childNodes.length;i++) { switch(xmlObj.childNodes(i).tagName) { case ‘quiz_title’: quiztitle=xmlObj.childNodes(i).text; break; case ‘quiz_send_to’: sendTo=xmlObj.childNodes(i).text; break; case ‘quiz_show_points’: show_points=xmlObj.childNodes(i).text; break; case ‘quiz_question’: // get the main elements of the Quiz Question question=xmlObj.childNodes(i).childNodes(0).text; questionType=xmlObj.childNodes(i).childNodes(1).text; questionAnswer=xmlObj.childNodes(i).childNodes(2).text; points=xmlObj.childNodes(i).childNodes(3).text; possibleAnswers.length=0; // reset the array storing the possible answers // if we have possible answers – i.e. a non TEXT type field if (xmlObj.childNodes(i).childNodes(4).tagName==’possible_answers’) { // load possible answers for (j=0;j<xmlObj.childNodes(i).childNodes(4).childNodes.length;j++) { possibleAnswers[j]=xmlObj.childNodes(i).childNodes(4).childNodes(j).text; } // load the responses for (j=0;j<xmlObj.childNodes(i).childNodes(5).childNodes.length;j++) { displayAnswers[j]=xmlObj.childNodes(i).childNodes(5).childNodes(j).text; } } // otherwise just load the responses else { // load the responses for (j=0;j<xmlObj.childNodes(i).childNodes(4).childNodes.length;j++) { displayAnswers[j]=xmlObj.childNodes(i).childNodes(4).childNodes(j).text; } } // add the question – have to join the possibleAnswers array as Arrays are passed by reference not value in JS questionArray[i-3]=new questionRecord(i,question,questionType,questionAnswer,points,possibleAnswers.join("Ã,¬"),displayA nswers.join("Ã,¬")); break; } } } // support function for the XML Load script function verify() { // 0 Object is not initialized // 1 Loading object is loading data // 2 Loaded object has loaded data // 3 Data from object can be worked with // 4 Object completely initialized if (xmlDoc.readyState != 4) { return false; } } // Object constructor for the Quiz function questionRecord(qno, question, questionType, questionAnswer, points, questionOptions, displayAnswers) { this.qno = qno; this.question = question; this.questionType = questionType; this.questionAnswer = questionAnswer; this.points = points; this.questionOptions = new Array(); this.questionOptions = questionOptions.split("Ã,¬"); this.displayAnswers = new Array(); this.displayAnswers = displayAnswers.split("Ã,¬"); }
Building the User Interface
Now that we have the data in the system, we need to build the user interface for the program, we start by building the container for the Quiz questions, a standard HTML table, a footer control containing a Reset and a Mark Quiz button and some text identifying the author of the program. For the question listings, it is simply a case of looping through the contents of the questionArray array which contains all the questions and process each one in turn.
This functionality is managed by a function called build_quiz and can be seen in Listing C. Each question will be on a separate table row, and will have two columns, one for the question and the other for the possible answers.
Listing C
// this is the master function for the program function build_quiz() { // check for Parameters if (document.location.search.toString().length>0) { // add header document.write("<div id=’quiztitle’ name=’quiztitle’ class=’quiztitle’>" + quizTitle + "</div>\n"); document.write("<form name=’myQuiz’ enctype=’text/plain’>\n"); document.write("<table class=’quiz’ border=’2′>\n"); // add each question for (count=0;count<questionArray.length;count++) { add_question(count); } // add the RESET and SCORE buttons and the Copyright document.write("<tr><td colspan=’3′> </td></tr><tr align=’center’><td colspan=’3′><input type=’reset’ value=’Reset Quiz’> <input type=’button’ onclick=’markQuiz();’ value=’Score Quiz’></td></tr>\n"); document.write("</table>\n"); document.write("</form>\n"); document.write("<table width=’100%’><tr><td width=’100%’ bgcolor=’green’ align=’center’><font color=’white’>JSQuiz Program by <a href=’http://builder.com.com/5171-22-1049623.html’><font color=’white’>Greg Griffiths</font></a></font></td></tr></table>"); } // otherwise inform the user else { alert("Please provide the parameters in the URL as required."); } }
To make the application easier to interact with, alternate rows will be displayed in a different colour. This is achieved by the use of Cascading Style Sheets (CSS). There will be two CSS classes, tr0 and tr1 respectively. The code then looks at each record in the questionArray array, and displays the question and the answers, in the specified format. If required, the score for each question is also displayed. This code can be seen in Listing D, using this functionality with a sample colour scheme, you end up with a sample screen similar to Figure A.
Listing D
// this function adds each question to the screen function add_question(qno) { // add the header and the CSS Class document.write("<tr class=’tr" + (qno%2) + "’>\n"); document.write(" <td width=’50%’>\n"); // if we are showing the no. points per question if ((show_points=="true")||(show_points=="TRUE")) { document.write("<span class=’question’>" + parseInt(qno+1) + ") " + questionArray[qno].question + " (" + questionArray[qno].points + ")</span>\n"); } else { document.write("<span class=’question’>" + parseInt(qno+1) + ") " + questionArray[qno].question + "</span>\n"); } document.write(" </td>\n"); document.write(" <td>\n"); // add the Question depending on Type switch (questionArray[qno].questionType) { case "dropdown": document.write("<select name=’q" + qno + "’>"); for (i=0;i<questionArray[qno].questionOptions.length;i++) { document.write("<option value=’" + i + "’>" + questionArray[qno].questionOptions[i] + "</option>\n"); } document.write("</select>\n"); break; case "radio": for (i=0;i<questionArray[qno].questionOptions.length;i++) { document.write("<input type=’radio’ name=’q" + qno + "’ value=’" + i + "’>" + questionArray[qno].questionOptions[i] + "<br>\n"); } break; case "checkbox": for (i=0;i<questionArray[qno].questionOptions.length;i++) { document.write("<input type=’checkbox’ name=’q" + qno + "’ value=’" + i + "’>" + questionArray[qno].questionOptions[i] + "<br>\n"); } break; case "text": document.write("<input type=’text’ name=’q" + qno + "’ size=’30′><br>"); break; case "select-many": document.write("<select multiple name=’q" + qno + "’>"); for (i=0;i<questionArray[qno].questionOptions.length;i++) { document.write("<option value=’" + i + "’>" + questionArray[qno].questionOptions[i] + "</option>\n"); } document.write("</select>\n"); break; } document.write(" </td>\n"); // add the empty cell for the answer document.write(" <td>\n") document.write(" <div id=’answer" + qno + "’ name=’answer" + qno + "’> </div>\n"); document.write(" </td>\n"); document.write("</tr>\n"); }
Scoring the Quiz
Now that the quiz has been displayed to the user, the next set of functionality revolves around the marking of the quiz and the reporting of the result to the user and also potentially to one or more others via e-mail.
The marking of the quiz is handled by a function called markQuiz, this function handles each question type independently and records the user’s running total and the total possible mark. Also, for each question, the correct answer is displayed along with a message to the user — this is defined in the displayAnswers array. The user is then informed of their score and the potential highest score.
If the results are being e-mailed to anyone else, then the e-mail is sent, the body of which was created as each questions answer was validated. As the quiz program runs client side, then the e-mail needs to be generated and sent from the client side. Thus the quiz program makes use of the mailto protocol and some of its functionality to send the e-mail from the client side. However, this approach can be unreliable and problematic and is often not the best solution, as discussed in several places that can be found via Google, but as the requirement is for 100% client side it is the only option. This was raised with the client and a server side approach was recommended for e-mail notification, but decided they did not want it and were happy with a completely client side approach.
Extensions
We have now created a very simple quiz program that can be reused simply by changing the XML Question file that goes with it. During the development, I considered several possible extensions to the program that I’ve not had time to implement thus far, they include:
I hope to have the time to look at these over the next few months, but I’d also like to hear from you, the readers. If you have any comments, feedback, changes or additions to this program please send them to me in an e-mail or in the discussion forum, that way I can learn from your experience as well as my own.
Greg Griffiths is a Web applications developer for a large multinational, working with several languages-from Perl to VB. He also works in knowledge and document management.
Theme: Rubric. Blog at WordPress.com.