Using JavaScript and jQuery to Configure an Anchor Tag

We recently helped one of our Exam Engine customers with a custom question template. In this template, they wanted to have a smaller version of an image. The user could then click on the smaller image to see a larger version in a popup window. The first task was to add the anchor tag to the HTML as shown below. The new lines are shown in bold.

<a id=”questionGraphicLink” target=”questionGraphicWindow”>
    <img id=”questionGraphic” src=”” alt=”” />
</a>

Next, we used the fact that the larger image would match the name of the smaller image except for a _lg at the end of the name. So if the question image were “xyz.png”, then the large image would be “xyz_lg.png.” We thus added the code below to the function that is called when the page is fully loaded.

// make hyperlink out of questionGraphic
            if (questionDictionaryId["QuestionGraphic"] != null) {
                var lgGraphic = questionDictionaryId["QuestionGraphic"].replace(".png", "_lg.png");

                $("#questionGraphicLink").attr("href", ("../media/" + lgGraphic));
            }

The questionDictionaryId JavaScript objects holds all the items for the question. If it has a “QuestionGraphic” item, we put a reference to the large graphic in the lgGraphic local variable. We then use jQuery to find a reference to the questionGraphicLink anchor object. We then set its href attribute to a relative path to that graphic. If the question does not have a graphic, we skip this step and thus avoid any kind of broken links.

Making a jQueryUI Button

We now use the nice jQueryUI library in Tracker.Net, Exam Engine, and Training Studio. In each case, you link in the correct JavaScript files and style sheet and then use JavaScript to create the button. The nice thing is that you can use various types of HTML objects (input, anchor, ASP.NET buttons, etc.) and they still end up looking like a button. Here is an example from Tracker.Net:

$(function () {
        var optionsBtnId = $("#optionsBtn");

        if (optionsBtnId.length) {
            optionsBtnId.button({
                icons: {
                    primary: "ui-icon-home"
                },
                text: false
            });
            optionsBtnId.click(function () {
                var returnVal = true;

                if (isOnCoursePage == true) {
                    if (isOneCourse == true) {
                        window.resizeTo(800, 600);
                    }
                    else {
                        returnVal = checkIfOkToLeave(LessonOpenMessageLeavingScreen);
                    }
                }

                return returnVal;
            });
        }
    });

The $() means that the jQuery will call the function after the page fully loads. We then get our hands on the HTML object that we want to use for our button. The #optionsBtn means that we are looking for an object with an id of “optionsBtn.” We then call the jQueryUI button method to create the button. In this case, we tell it to use the jQueryUI home icon and not to have any text. Next, we define the click function. Here we are doing some logic to determine whether to proceed with the click. If returnVal is false, the server side event will not be processed and we stay on the page.

JavaScript and jQuery in Training Studio

The new version of Training Studio is completely HTML and JavaScript. Rather than using ActionScript to create interactions as in past versions, we now use JavaScript. In particular, we take advantage of the jQuery library to make it even easier than in past versions. Let’s look at an example. The script below is the entire JavaScript for the buttonClickLeftShowContentImageMediatemplate. The interaction is “buttonClick” on hotspots. In response to the click, the template shows content (text), an associated image, and/or plays associated media. I will make some comments/explanations in-line.

var lastHotspot = null;
var completionArray = [];
var numHotspots = 0;

Since these variables are declared outside a function block, they are effectively global to the page. They are used to keep track of completion and the “last” hotspot accessed (in order to set its style to HotspotCompleted).

$(function () {
	var pageArrayLocal = parent.pageArray; // associative array
	var keyId;
	var keyString;
	var keyValue;
	var objectSelector;
	var objectId;
	var hotspotNum;

The $ refers to jQuery. This function is called once the page completely loads. The reference to parent is to the index.htm page. Since it references TrainingStudioSupport.js, the parent.pageArray means that we are reading the global variable from that file. This pageArrayLocal variable is basically a dictionary that represents the current training page. The key matches up to the node or column in the database. They are title, subtitle, content_0, etc.

	parent.ShowTransition();

As with the pageArray variable, the line above calls the showTransition function in TrainingStudioSupport.js. This just shows the iFrame holding the template.

	for (keyId in pageArrayLocal) {
		keyString = keyId.toString();
		keyValue = pageArrayLocal[keyId];

		// handle unique ones here. Handle the rest in PopulateTemplate (TrainingStudioSupport.js)

We loop through each of the keys (content_0, media_0, etc.) and only handle the ones that need special handling by this template.

		switch (keyString) {
			// let "media_0" get handled by template       
			case "media_1":
			case "media_2":
			case "media_3":
			case "media_4":
			case "media_5":
			case "media_6":
			case "media_7":
			case "media_8":
			case "media_9":
			case "media_10":
				// let graphic_0 be handled by template   
			case "graphic_1":
			case "graphic_2":
			case "graphic_3":
			case "graphic_4":
			case "graphic_5":
			case "graphic_6":
			case "graphic_7":
			case "graphic_8":
			case "graphic_9":
			case "graphic_10":
				break;

We let standard PopulateTemplate (see below) method handle media_0 and graphic_0, since we want any initial sound, video, or animation to play and any initial graphic to display. The rest of the media (media_1 – media_10) and graphics (graphic_0 – graphic_10are only played/displayed in response to the hotspot interaction. So we don’t do anything except break when we encounter them.

			case "hotspot_1":
			case "hotspot_2":
			case "hotspot_3":
			case "hotspot_4":
			case "hotspot_5":
			case "hotspot_6":
			case "hotspot_7":
			case "hotspot_8":
			case "hotspot_9":
			case "hotspot_10":
				var contentId = parent.formatHotspot(keyValue);

				objectName = "#" + keyString;
				objectId = $(objectName);

				hotspotNum = parent.getFieldNum(keyString);
				numHotspots = Math.max(hotspotNum, numHotspots);
				objectId.html(contentId);
				objectId.show();
				objectId.click(hotspotClickHandler);

				break;

We first build a reference to the associated span object using jQuery. A jQuery reference to an object with an id of “hotspot_1” looks like this: $(“#hotspot_1”); In jQuery terms, the “#hotspot_1” is the selector. We call the formatHotspot() method of the TrainingStudioSupport.js using parent once again. This method looks for special bullet and hyperlink characters and returns the proper HTML. After creating our jQuery object reference, we strip the number (1, 2, 3, etc.) from the name of the object and use it to populate our hotspotNum variable. We keep a running total of the numHotspots as well. We use this to determine when all the hotspots have been selected. Finally, we associate the control’s click event with our hotspotClickHandlerfunction. This is what makes something happen when the user clicks on the hotspot.

			default:
				objectName = "#" + keyString;
				objectId = $(objectName);
				parent.PopulateTemplate(objectId, keyString, keyValue);

This line is where all the keys that we didn’t specifically handle are sent to PopulateTemplateinstead. This avoids duplicate code in every template.

		}
	}
	// bind keyboard events
	$(document).keydown(parent.ImplementKeyDown);

This line associates the keydown event with the ImplementKeyDown function in TrainingStudioSupport.js. This allows us to go forward with the PageDown key and backwards with the PageUp key. It also shows the Comment Editor if the reviewOnvariable is true and the user presses Ctrl + Shift + M.

});

function hotspotClickHandler(e) {
	var targetId = $(this);

We use the jQuery selector, $(this), to figure out which hotspot (span) the user interacted with.

	var displayFieldId = $("#displayField");

Similarly, we make a reference to our “display field” object. We use this to set its text based on a naming scheme. When the user clicks on hotspot_1, we want to display any text in content_1.

	var hotspotName = targetId.attr("id");
	var hotspotNum = parent.getFieldNum(hotspotName);

We grab the id using the jQuery attr() function. We then find the associated number in order to work our naming scheme. hotspot_1 goes with media_1, graphic_1, and content_1and so on.

	if (lastHotspot != null) {
		lastHotspot.attr("class", "HotspotCompleted");
	}

The first time through, the lastHotspot variable will be null. After that, it will refer to the hotspot previous to this interaction. In that case, we set its class to “HotspotCompleted.” This is how we get it to turn blue or otherwise show completion.

	lastHotspot = targetId;

We set the lastHotspot variable so we’ll be able to change its class the nexttime through.

	targetId.attr("class", "HotspotCurrent");

We change the class of this hotspot to “HotspotCurrent” to denote which one we are currently looking at.

	completionArray[hotspotNum - 1] = true;

We set the associated element (subtracting 1 since the array is zero-based) of our completionArrayto true. Once all the elements are “true,” the page is completed.

	parent.showTextImageMedia(displayFieldId, hotspotName, $("#graphic_0"), $("#media_0"), true); // include media

We call the showTextImageMedia method to display the associated content, play any associated media, and display any associated graphic. Note that we pass the object references to display the content (displayFieldId), show the graphic ($(“#graphic_0”)), or play the media ($(“#media_0”)). The parameter at the end determines whether to include media.

	parent.getHotspotCompleted(completionArray, numHotspots);

We pass our completionArray and the numHotspots variable to the getHotspotCompletedmethod. This will show a “completion” image if all the interactions are completed.

}

Using jQuery UI Along with ASP.NET Web Forms

I like the jQuery UI objects, particularly its Button and Datepicker widgets. But what if you want to use them on a normal ASP.NET web form? Back in the early 2000’s, I wrote the VBTrain Graphical Button control to generate all the JavaScript needed to swap out graphics for the up, down, over, and disabled states. But now jQueryUI does all of that for us. Making the two technologies work together is pretty simple. Here is what the input control looks like:

<input id="SubmitBtn" runat="server" value="Submit" type="submit" />

The only change we made was the runat=”server” part. This is what allows us to recognize it in server-side code.

The next step is to add jQuery UI. We do this with the appropriate .js and css files loaded in the head of the file. We are using the Start theme, which is why the reference is “css/start/.”

<link href="css/start/jquery-ui-1.8.17.custom.css" rel="stylesheet" type="text/css" />
<script src="scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="scripts/jquery-ui-1.8.17.custom.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(function () {
        $("input:submit").button();
    });
</script>

You might recognize the “load” function from the previous article. Once the page is fully loaded, it executes. We then use a new type of selector: “input:submit”. This means that we find all input controls that have a type of submit. We then call the button() method on each one. This is what loads all the appropriate graphics and associated scripts. If there had been 10 input buttons (of type submit) on the page, that one line would have configured all of them.

The last step is handling the click in our “code behind” file. That handler is shown below.

Private Sub SubmitBtn_ServerClick(sender As Object, e As System.EventArgs) Handles SubmitBtn.ServerClick
    ' take action here
End Sub

Those of you who are used to normal ASP.NET buttons will see that the event is a bit different: ServerClick rather than the normal Click. But the functionality is exactly the same.

JavaScript and jQuery in Exam Engine 4

The new version of Exam Engine is completely HTML and JavaScript. The navigation buttons and other common elements are in the main page (index.htm) as is an iFrame that contains each of the templates. One of these templates is login.htm, which is used if the developer has specified to use a login page in the absence of an LMS. Let’s look at that page to get an idea of the design and how the jQuery library comes in handy.
Here is the HTML of the login.htm page.

<body>
    <span id="loginLabel"></span>
	<img id="loginImageBtn" src="" alt="" />
	<input id="loginBtn" type="button" />
	<input id="studentName" type="text" />
	<input id="studentPassword" type="password" />
	<span id="statusBlock"></span>
</body>

The position, font, and other formatting are implemented with a style sheet. But we need to add the functionality with JavaScript. Here is the “load page” script:

var usePassword;

$(function () {
	var loginText = parent.ReadExamSetting("LoginPageMessage", "Enter your name:");
	var loginBtn;
	var studentNameId = $("#studentName");
	var passwordId = $("#studentPassword");

	usePassword = parent.ReadExamSettingBoolean("LoginPageUsePassword", false);

	$("#loginLabel").html(loginText);

	if (parent.UsejQueryUiButtons == true) {
		loginBtn = $("#loginBtn");
		$("#loginImageBtn").hide(); // needed for Firefox
	}
	else {
		loginBtn = $("#loginImageBtn");
		$("#loginBtn").hide(); // needed for Firefox
	}

	parent.ConfigureButton(loginBtn, "Login", LoginBtn_Click);
	parent.SetBtnStatus(loginBtn, "Login", true, true, true); // visible, enabled, in template

	if (usePassword == true) {
		passwordId.show();
		passwordId.keydown(studentName_KeyDown);
	}
	else {
		passwordId.hide();
		studentNameId.keydown(studentName_KeyDown);
	}

	// Hide Countdown Timer
	parent.CountdownTimerRef.hide();
	parent.TransitionControlRef.show();
});

We declare usePassword outside the function block so that we can use its value when the user actually clicks the Login button. The $(function() { syntax is part of jQuery and means that the page if fully loaded and all the objects are in place. We read our loginText variable with the ReadExamSetting function. The first parameter is the name of the setting and the second is a default value. This setting is originally created in the Exam Engine Configuration Editor and is stored in an XML file that is part of the exam. parent refers to the main page and its ExamEngineSettings.js file. The studentNameId and passwordId variables are jQuery objects. The “#studentName” is a selector that identifies an object with an id of “studentName.” This is the input control in the HTML above.

Next, we read the usePassword value from the exam as well. We then set the text of our loginLabel span control with this syntax: $(“#loginLabel”).html(loginText);. The first part creates the jQuery object using the selector as we saw above. We then call its html method and set its value to the loginText value that we read above.

Our next set of logic is to use either jQuery UI buttons or normal graphic buttons. We set the loginBtn variable to the appropriate object (input for jQueryUI or img for graphical) and hide the other object. At that point, we call two common functions, ConfigureButton and SetBtnStatus. These associate the proper “click” handler, set the states of the buttons, and load graphics if needed.

We then use our usePassword variable to either show or hide the password input control. Based on whether it is showing, we either add a KeyDown handler to it or the login input. This is so the user can press the Enter key rather than having to click or tab to the Login button. Notice the handy jQuery syntax to handle the keydown event with the studentName_KeyDown function: passwordId.keydown(studentName_KeyDown);.

We end by hiding the “countdown” timer and showing the iFrame itself (TransitionControlRef). The main point is that using jQuery simplifies getting our hands on object references and gives us a standard set of properties and methods.

Introduction to jQuery

I have been doing quite a bit of HTML and JavaScript and am very impressed with the jQuery library. It is free and is being adopted widely. I like it because 1) it keeps you from having to worry about cross-browser differences, 2) it makes coding in JavaScript more like using Visual Basic or OpenScript. Here is a short example to get you started. The HTML below has two buttons and four images. We want to show all the images when you click the showBtn and hide them when you click the hideBtn.

<body>
    <input id="showBtn" type="button" value="Show Images" />
    <input id="hideBtn" type="button" value="Hide Images" />
    <img id="Img1" src="media/help.png" />
    <img id="Img2" src="media/coda.png" />
    <img id="Img3" src="media/Desperado.png" />
    <img id="Img4" src="media/5150.png" />
</body>

Let’s write the logic with jQuery. We first add a reference to its .js file:

<script src="jquery/jquery-1.5.1.js" type="text/javascript"></script>

Next, we add the code below.

    <script type="text/javascript">
        $(document).ready(function () {
            $("#showBtn").click(function (e) {
                $(".ImageClass").show();
            });
            $("#hideBtn").click(function (e) {
                $(".ImageClass").hide();
            });
        });
    </script>

The $ symbol is the shortcut for making a jQuery call. The parameter is either an object like document or a selector like we will see below. So $(document) is a jQuery object that references the HTML document. We then define what to do when it is ready. This means that the page is fully loaded. This equivalent to Load in Visual Basic or enterPage in ToolBook OpenScript.

When ready, we configure what our two buttons do. This starts to show the real power of jQuery. The $(“#showBtn”) code gets the jQuery object reference to an HTML object with an id of “showBtn.” This is our button. We then define what to do in response to its click event. In that case, we use another jQuery selector, $(“.ImageClass”). The “.” means that we are looking for all objects with a class of “ImageClass.” We then call its show method. Notice that even though there are four of the objects (our images), jQuery takes care of calling the show method for each one. jQuery also takes care of how to show the object based on its type and the browser. This typically means changing the object’s display style.
Similarly, we find the hideBtn and defined to hide all the image objects in response to its click event.

This just scratches the surface of what jQuery can do. But hopefully, it gives you an incentive to learn more.