HTML5/jQuery experiments: LOST Redux gets a tune-up
While normal Americans stuffed their faces over the long Thanksgiving weekend I decided to finally dip my toe into the HTML5 pool. jQuery lured me even further into these unfamiliar waters, but ultimately I would have to sink or swim by experimenting with LOST Redux.
Day 1 was re-writing the site in MVC3, which was painless. Day 2 was the challenge: taking the Silverlight trivia quiz and making a completely plugin-free version.
It's tough to tell the difference between the versions, so to that end things went pretty well. The quiz now comes in a mobile-optimized version also, though I've only tested on an iPhone so far. If your mileage varies I'd appreciate any details you can pass along.
I jotted a few notes throughout the weekend and shared them below. I admit they're pretty random and unorganized, but who knows, they might help you avoid a snag or two down the road.
Disclaimer: this isn't an HTML5 showcase!
To be clear, my first priority is reaching as many browsers/devices as possible. Other than keeping the markup HTML5-compliant and experimenting with <audio> tags, I didn't implement most HTML5 bells and whistles. There's nothing cutting-edge going on (yet) but the stage is set.
How the quiz works: the visuals
Extensive CSS here, particularly absolute positioning and z-indexes. jQuery animates the position and opacity of elements. The toughest visual was the countdown timer, which is a set of <div>s that are clipped and animated.
A couple notes: 1) jQuery.animate() is easy to abuse. 2) if overflow:hidden wasn't completely supported I would be completely screwed. I have a series of container <div>s that clip their children; without that capability this would've been a complete bust.
How the quiz works: the workflow in a nutshell
Quiz games are one of the simplest workflows around, at least conceptually:
-
On initialization, the page calls an MVC Controller via jQuery.ajax(). The controller returns an array of arrays. Each array contains a set of question IDs based on difficulty level. Only the question IDs are returned in these arrays, no other metadata.
-
Random IDs are selected and removed from the client-side arrays. Levels auto-switch when array slots are exhausted.
-
Question IDs are sent to the Controller, which gathers the question/answer data and returns the generated HTML. As soon as the appropriate image has loaded, the HTML slides into view.
-
When the game ends the process begins anew, including initialization.
jQuery + AJAX: lessons learned
Most of the hair-pulling moments came when I was setting up the JSON pipeline. To fetch the initial questions I used code similar to what you'll find on the jQuery API page:
$.ajax(
{
type: "POST",
url: rootPath + "quiz/initialize",
success: function (data) { onInitializeSucceeded(data); },
error: function (data) { onInitializeFailed(data); },
contentType: "application/json; charset=utf-8",
dataType: "json"
});
So far so good. The POST goes to an MVC Controller. A repository fetches the question IDs:
public object[] GetQuestionIDs()
{
var questions = (from q in _db.quiz_Question select q).ToList();
return new object[] {
GetQuestionIDs(ref questions, 1), // easy
GetQuestionIDs(ref questions, 2), // standard
GetQuestionIDs(ref questions, 3), // tough
GetQuestionIDs(ref questions, 4) // insane
};
}
... and the Controller returns JSON to the page:
[HttpPost]
public JsonResult Initialize()
{
return Json(new QuizRepository().GetQuestionIDs());
}
But I hit a wall when it came to the "post score" feature, which requires passing multiple bits of data to the controller. The code below resulted in errors before it could even reach the controller:
$.ajax(
{
type: "POST",
url: rootPath + "quiz/postscore",
success: function (data) { onPostScoreSucceeded(data); },
error: function (result) { onPostScoreFailed(result); },
dataType: "json",
data: {
name: nickname,
tracker: correctTracker,
score: score
}
});
Long story short, all I needed to do was add the following magical line to the above set:
Apparently my expectations of JSON object serialization are considered the "traditional" approach. This raises the question of why it must be explicitly specified if it's traditional, but I digress. I'm a little nervous relying on this with jQuery constantly being updated, but it'll do for now. Eventually I'll research the new and exciting ways of sending JSON data over the wire. Yes, that was tongue-in-cheek.
HTML5: looks interesting, so let's get moving
I'm looking forward to sinking my teeth into HTML5's new elements, especially the <canvas> and new input elements. But for the purposes of Lost Redux I was happy just to have the <audio> tag and not having to rely on plugins or <bgsound>.
The <audio> tag is the perfect example of a web developer's struggles. The MP3 audio format is patented and thus expensive, so to target all major browsers you must create and reference multiple versions of the same sounds:
<audio id="audBeep" preload="auto">
<source src="@Url.Content("~/media/beep.mp3")" type="audio/mpeg">
<source src="@Url.Content("~/media/beep.ogg")" type="audio/ogg">
</audio>
<audio id="audAlarm" preload="auto">
<source src="@Url.Content("~/media/alarm.mp3")" type="audio/mpeg">
<source src="@Url.Content("~/media/alarm.ogg")" type="audio/ogg">
</audio>
The Vorbis Ogg format is the patent-free, open-source fallback for browsers such as Firefox. But there's another head-scratcher besides the patent issue: Safari will not play your sounds unless they are triggered directly as a result of user interaction. If you play the quiz on an iPhone you'll notice that only the buzzer sound plays, because it's the result of clicking a wrong answer. The countdown beep does not play because it's scheduled on an interval. Safari's the only browser I've run into so far that enforces this restriction.
All in all, a good learning experience
I'm not going to lie and say JavaScript's as pleasant to work with as Silverlight. It isn't, not even with jQuery covering up the smell. The day JavaScript graduates to a first-class citizen in an established IDE (browser debugging tools don't quite cut it), my opinion might change. That said, all my missions were accomplished. I learned quite a bit about jQuery, got more practice with MVC, and finally started exploring HTML5. These are uncertain times for a web developer, so the best way to offset the anxiety is to diversify one's skillset as much as possible.
|