On Converting Flash to HTML

I received a question from Bob (no, really), who wrote:

I have a question about the newest version of Flash and its HTML publishing option using CreateJS. What do you think of that approach going forward?

I started to write an email response but figured I should probably post it here.

I haven’t been paying much attention to Flash, so I don’t know what the ‘HTML export’ is capable of these days. In general, I’m very wary of converting Flash-based projects to HTML. When Adobe Captivate first released a “publish to HTML5” feature, all it did was convert the SWF animation to a video file, losing all interactivity along the way.

The limitations of the browsers and the HTML5 spec mean you can’t expect a fully 1:1 conversion from Flash to HTML, regardless of libraries like CreateJS. Some of the features found in Flash are still not quite supported in browsers, or might not work quite the way you’d expect. For example, CSS transitions, CSS gradients, and SVG/Canvas support vary widely from browser to browser (though it’s getting better, and there are workarounds aka “polyfills”). Streaming video, which is a breeze in Flash, is not part of the HTML5 Video spec (yet) and is unsupported in browsers. Video and audio codec support is inconsistent. In some cases, devices add new limitations — last time I checked, iOS devices wouldn’t autoplay audio or video in Safari. ‘Play’ couldn’t be scripted, it required the user to press a button.

Publishing to HTML (with the aid of JavaScript libraries like CreateJS) is definitely the way of the future, but I would flip the workflow: use a tool that’s “HTML first”. For my workflow, I always start in HTML then only fall back to Flash if I absolutely have to. I hand-code, but if you want to stick to a WYSIWYG editor, maybe try some of the Adobe Edge products, or go with a third-party product such as Tumult Hype.

If you continue to use Flash as an HTML development tool, temper expectations and test widely, as some things might not work the way you expect when converted to HTML.

And regardless of whether you publish to Flash or HTML, always test the accessibility of your project. Just because it’s HTML doesn’t mean it’s accessible; HTML by nature is more accessible than Flash, but libraries like CreateJS add a lot of complexity to the page, which can easily impact accessibility.

Advertisements

Cleaning up Adobe Captivate’s SCORM Publishing Template, Part 3: JavaScript

In part one of this series, we published a simple Captivate course and examined its file structure. In part two, we cleaned up the HTML file and externalized all JavaScript. Today we will clean up the JavaScript.

Cleanup task #1: Tidy things up

Personally, I like to clean up code whenever I can; I feel it helps to be consistent with your code style. For example, do you place brackets { } on a new line, or in-line? Do you skip them once in a while? Are you consistent with your semicolons and line breaks?

I’m going to quickly run through this code to add brackets where needed, clean up usage of tabs, spaces, and other whitespace, and clean up any other little messes or inconsistencies I might find, without refactoring the code.

Here’s the ‘before’ code, if you’d like to follow along. Here’s the same code after it has been cleaned up.

Not perfect, but it’s a start.

Cleanup task #2: This code is VARy verbose

Here a var, there a var, everywhere a var var. There are way too many var statements in this code, some of which re-declare a variable that has already been declared! Minimizing vars is a great first step for refactoring. I will remove vars where possible, and also group and re-order where it makes sense.

While we’re at it, let’s get those function blocks out of the conditional statements.

View the result of task #2

Cleanup task #3: Replace SWFObject 1.5 with SWFObject 2.2

This template uses SWFObject 1.5, which while very durable, is also very outdated. SWFObject 2.2 provides additional features that we can put to use in our template, including a DOM-ready function and browser detection.

I’ll be linking to the copy of SWFObject stored in the Google Libraries API. Why? It loads quickly, and it might already be cached in the visitor’s browser, and it’s one less chuck of code for me to manage. No worries, though — if you don’t like the idea of using Google’s service, just paste the SWFObject 2.2 source code into standard.js, replacing the original SWFObject 1.5 code.

Next we need to replace the SWFObject 1.5 syntax in captivate.js with SWFObject 2.2 syntax. Since SWFObject 2.2’s embedSWF method includes domready-style event handling, we don’t need to wrap the SWFObject code in a window.onload event. We can also use SWFObject’s callback functionality to invoke the focus() method.

Aside: only form elements and links can receive focus; to enable other page elements to receive focus, you must set the element’s tabIndex property. The original Captivate code did not handle this task, so we will handle it here by setting tabIndex to -1, which enables us to use JavaScript’s focus method without disrupting the browser’s default tab index order. Read more about tab index here.

View the results of task #3

Cleanup task #4: What’s with the sniffing?

Line 23 of captivate.js sniffs for Internet Explorer by looking at navigator.appName. Line 207 does the same thing, with extra checks, including a check for Windows 3.1.

No, seriously:


navigator.userAgent.indexOf("Windows 3.1") == -1

(What decade is this?)

Since SWFObject 2.2 includes browser detection, we should replace all IE checks with SWFObject’s swfobject.ua.ie property. If you really stop to think, you should be asking yourself “but why are we checking for IE? Why should it receive special handling?” The goal is to use cross-browser code that works everywhere without hacks.

In two of the three IE checks, we see this:


var CaptivateObj = swfobject.ua.ie ? Captivate : document.Captivate;

Umm… yeah, more code from 1998. We can do better by simply using the W3C standard document.getElementById. This is 2012, all browsers support document.getElementById now.

View the result of task #4

Cleanup task #5: Keep your comments to yourself

There are a lot of bits of code that have been commented out. Since these are never used, let’s get rid of them.

Cleanup task #6: Tame the variable definitions and string manipulation

Variable definitions are strewn all over, with some being nested in if/else blocks. Let’s simplify where we can using ternary conditional statements or by refactoring to remove the variable, if possible.

There is also a lot of clever string manipulation going on around the document.location object, specifically with the variables strURLBase, strURLParams, strURLFull, intTemp, and strTemp. If you follow the code through the document, you’ll see a huge glaring problem when you get to line 259. All of that convoluted document.location parsing and string manipulation exists solely to define the value of strURLParams, yet when we get to line 259, we see:


if(g_objAPI != null){
   strURLParams = "?SCORM_API=" + g_zAPIVersion + "&SCORM_TYPE=" + g_intAPIType;
}

This line says “if the SCORM object is not null, give strURLParams the following value, overwriting any previous value.”

Since this is a SCORM 2004 course, if the SCORM object is null, the course is dead in the water. Therefore all of that hullabaloo around defining strURLParams is useless, except the part at line 259.

Also, the strURLParams variable is meant to hold value/key pairs found in the page’s URL; our template files never append querystring values to the HTML file, so there’s no point in looking for querystring parameters that will never be used. Let’s delete it… all of it!

View the result of task #6

Cleanup task #7: eval() is evil.

Any JavaScript developer worth his salt will tell you that eval() should only be used in very very very limited circumstances (such as parsing JSON). Most JavaScript developers will tell you it should be avoided completely. I’m one of them.

eval() is a security risk, and is often used as a shortcut by lazy coders. Captivate’s JavaScript (and ActionScript) is chock full of eval statements. Let’s refactor the code to avoid using eval(). It has the additional benefit of making the code more readable, too!

document.write is considered almost as bad as eval, but we’ll leave it in there for now — its sole purpose is handling VBScript in Internet Explorer, which is likely to be removed in a later post.

View the result of task #7

Cleanup task #8: SCORM 1.2 has no business here

This template was adopted from a SCORM 1.2 template, and contains lots of conditional checks to see if the course is using SCORM 1.2 or 2004. Since we know this is a SCORM 2004 template, we can remove all code related to SCORM 1.2. Captivate maintains separate templates for SCORM 1.2 and SCORM 2004 anyway, so it makes no sense to have code for both SCORM versions floating around in the page.

View the result of task #8

Our captivate.js file is now under 200 lines of code!

Cleanup task #9: Organize the SCORM code

As you recall, we created the captivate.js file as a place to store our external JavaScript. However, now that it has been pared down to a manageable and easier-to-read state, we can see that most of it is SCORM-related; since there’s already a scorm_support.js file, it’s only logical that we move our SCORM-related JavaScript from captivate.js to scorm_support.js.

View the results of task #9:
captivate.js
scorm_support.js

Cleanup task #10: Clean up scorm_support.js

Now that we’re getting into the scorm_support.js file, we should clean it up as well by applying all of the cleanup techniques and approaches we’ve used so far.

You can see a slightly cleaned up version here.

In the part four of the series, we’ll dig in to the JavaScript portion of the SCORM code, examining the FS Command and ExternalInterface code contained in the Captivate template. Continue to part 4.

Cleaning up Adobe Captivate’s SCORM Publishing Template, Part 2: HTML

In part one of this series, we published a simple Captivate course and examined its file structure. In this part, we’ll take an in-depth look at the HTML generated by Captivate (using the SCORM 2004 publishing template) and clean it up as much as we can.

Here’s the default HTML published by Captivate.

I’ll take the high road and avoid the ever-so-easy snarky comments about this code. Suffice to say it needs some serious cleanup; it’s obvious that multiple people with multiple coding styles have worked on this document, and no quality assurance team has run through it to ensure it’s clean and concise. For example, there is an entire block of code commented out at line 141 (commenting it out means it never gets used, and therefore is useless bloat).

There’s also an aborted attempt at using SWFObject from lines 214 to 242 — the object is declared, but never invoked. Never. Did you know this code has been shipping with Captivate for years? I’m amazed no one has bothered to clean it up (and yes, I have reported it to Adobe, but they chose not to act).

As irritated as I get looking at the code, this blog post is not meant to be a rant, so let’s be constructive and get to work cleaning this mess.

Cleanup task #1: Externalize the JavaScript

This document is incredibly hard to read due to all of the <script> elements and JavaScript code. Let’s start by removing the JavaScript in the document’s <head> by cutting it out and pasting it into an external JavaScript file.

View the results of task #1:
The new captivate.js file
The slightly cleaner sample.htm file

This single step makes the HTML file much easier to read.

Cleanup task #2: Fix the doctype

Stepping through the HTML, the very first lines of code (lines 3-4) contain a conflict: the doctype says this is an HTML 4 (transitional) document, but the head element contains an XHTML lang declaration.

We’re in an HTML5 world now, so let’s use the HTML5 doctype. It’s 100% compatible with all browsers (even Internet Explorer 6), and all the cool kids use it. We can retain the lang declaration, too, with a few modifications. While we’re at it, let’s update the charset meta tag to the accepted HTML5 syntax.

View the result of task #2

Cleanup task #3: End the crazy <script> abuse!

There are six <script> blocks in our HTML file, and they are all inconsistent. For example, some have a “type” attribute, some have a “language” attribute, some have both, and some have neither. This is a sure sign of multiple authors working on the document over a long period of time.

Going through the <script> block, we can clean them up as follows:

  • Line 8: Since we’re using the HTML5 doctype, we can simplify it to remove the “type” attribute.
  • Line 9: Leave as-is.
  • Line 10: Remove the “type” and “language” attributes.
  • Lines 17-25: This entire block is never invoked and is completely useless. Delete it.
  • Lines 37-73: The contents of this block need cleanup, but we’ll handle that later one. For now, just remove the “type” and “language” attributes.
  • Lines 74-77: There is no need for a separate <script> block here; merge the contents with the previous <script> block.

View the result of task #3

Cleanup task #4: Remove inline scripts and styles

The <body> element contains two inline attributes: a style attribute, and an onunload handler. It’s an accepted best practice to leave your markup as clean as possible by removing inline styles and scripts when possible, so let’s create a new <style> element to hold the CSS, and move the onunload handler to our captivate.js file (just paste window.onunload = Finish; at the bottom of captivate.js).

View the result of task #4

If you’re not sleeping yet, you’re probably wondering why creating a <style> block is better than leaving the style attribute in the <body> tag. For this project, I know I will be adding more CSS declarations to the <style> block, so we may as well set it up now.

Cleanup task #5: Cure <center>itis

There are two sets of <center> elements on the page that can be replaced by adding one line of CSS to the top of the page.

Delete lines 16, 47, 66 and 72. Add text-align: center; to the body selector in the <style> block.

View the result of task #5

Cleanup task #6: Edit the <noscript> block

For accessibility and usability purposes, it’s usually a good idea to include a <noscript> block; this enables you to present a message or fallback content to visitors who have JavaScript disabled in their browsers.

Captivate uses SWFObject — a JavaScript utility — to embed the Captivate SWF. To hedge their bets, Adobe also attempts to embed the SWF in the <noscript> block using a method that doesn’t require JavaScript. Normally this is a good idea. However, SCORM courses require JavaScript; if JavaScript is disabled, the course won’t be able to communicate with the LMS. Period. End of story.

For this Captivate template, it’s probably best to remove the SWF from the <noscript> block and leave a message for the learner informing them that they can’t take the course unless JavaScript is enabled. Chances are they can’t get to the popup window containing the course anyway!

View the result of task #6

Cleanup task #7: Get rid of the <table> markup

The JavaScript on lines 39-41 and 54-56 generate a <table> whose sole purpose is to make the SWF vertically centered on the page. For our purposes, this is unnecessary because most courses launch in a popup window that has been sized to fit the course.

Ironically, the <table> that is meant to make the embedded SWF look nice on the page actually causes a significant problem — the table has cellpadding and cellspacing set to 7, which means the SWF will never extend to the edge of the window. When you’re trying to make a perfectly-sized popup window for your course, this cellpadding and cellspacing will cause scrollbars to appear, driving you bonkers!

Also, it’s widely accepted that tables shouldn’t be used for layout purposes, anyway, so it’s best to get rid of the <table>.

View the result of task #7

Our HTML file is starting to look pretty sweet!

Cleanup task #8: Externalize more JavaScript where possible

We externalized JavaScript in steps 1 and 4, let’s try and get the rest of this JavaScript externalized. The only remaining inline JavaScript is contained in lines 22 through 51. Our first task is to determine whether it needs to be inline; will moving it break anything?

  • Lines 23 through 36 appear to be safe to move into the document’s <head>, which means it’s safe to move to our captivate.js file. Just cut and paste — place the script at the very bottom of captivate.js.
  • Lines 38 through 47 use SWFObject 1.5 to embed the SWF. We can move this code, but it will need to be wrapped in an onload event or else it will break the page. In a later step, we will upgrade SWFObject to 2.2, which helps us optimize the Captivate template even further.
  • Lines 49 and 50 can be moved, but will also need to be wrapped in an onload event.

While moving the SWFObject code, we can also clean it up a bit:

  • Line 39 is not necessary because quality = high is Flash Player’s default.
  • Line 41 is not necessary because SWFObject is already set to use “Captivate” (the second argument passed on line 38) as the ID for the SWF.
  • Line 42 is not necessary because wmode = window is Flash Player’s default.
  • Line 45 is not necessary because it does nothing, it’s a completely gibberish line of code. I suspect it was a placeholder put there by a developer years ago, and no one has bothered removing it. It has shipped with Captivate for years.
  • Line 46 is useless; the redirect URL is only invoked when two criteria are met: 1) the embed markup is present in the HTML, and 2) the visitor does not have the required version of Flash Player. This Captivate file is using SWFObject to write the embed code. SWFObject has its own check for Flash Player (argument #5 on line 38); if the minimum version specified is not found, SWFObject will not write the embed code into the HTML. This means requirement #1 listed above will never be met, which means the redirect URL will never be invoked.
  • Update: Upon re-reading this post, I noticed even more issues with the SWFObject code: line 40 (‘name’ is not a param, it’s an attribute) and line 43 (bgcolor param isn’t needed, the color should be specified as the 6th argument in the SWFObject declaration). I won’t update the examples because we’re replacing SWFObject 1.5 with 2.2 in the next part of this series, so the entire SWFObject code block will be replaced.

    The updated HTML:

    Nice and simple, huh? We can pare it down more later, but this is a great start.

    View the updated captivate.js file.

    We were able to reduce the sample.htm file from 278 lines of code to 23 lines. Our new captivate.js weighs in at 224 lines of code, but we’ll be taking a sledgehammer to the JavaScript code in part three of this series.

    Continue to part three.

Cleaning up Adobe Captivate’s SCORM Publishing Template, Part 1: Introduction

Adobe Captivate is an enormously popular tool for e-learning developers. My assumption is that most Captivate users chose Captivate as their development tool because it enables them to publish LMS-compatible courses without requiring any programming skills — no need to know JavaScript, ActionScript, SCORM, etc.

This might explain why no one is up in arms about the astoundingly bad HTML and JavaScript output by Captivate — very few people look under the hood.

In this multi-part series, I will walk through the files Captivate outputs when publishing to SCORM 2004, pointing out the bad parts (hey Adobe: this is constructive criticism, it’s for your own good) and suggesting alternatives when needed. At the end of the series, I will provide a fully-functional SCORM 2004 publishing template you can use with Captivate 5.5.

Getting Started

The first step is to create a very simple Captivate course and publish it using the SCORM 2004 template. This will produce a set of files that we can examine and modify without breaking any templates.

  1. Go to File > New Project > Blank Project
  2. Select a small screen size since we’re just messing around. I selected 640 x 480.
  3. Add something to the stage just so we have something to look at aside from a blank white screen. You can add a text caption, image, etc. — it doesn’t have to be fancy, it just gives us something to look at.
  4. Publish to SCORM 2004
    1. File > Publish Settings
    2. Quiz > Check “Enable reporting for this project”
    3. Under Learning Management System select SCORM.
    4. Click the “Manifest” button. The Manifest dialog will appear; select 2004 from the drop-down menu.
    5. Click OK, then click OK in the Publishing Settings dialog.

Now publish your course: File > Publish. The Publish dialog appears. Nothing to change here, unless you want to select a different location for your files. Click “Publish”.

Attack of the Files

Find the folder containing your published files and take a peek at what Captivate generated for you. It’s quite intimidating, especially with all of those SCORM 2004 XSD files. I suggest sorting the files by kind.

Here’s a quick breakdown of the files:

  • All the DTD and XSD files are a required part of SCORM’s packaging system. The common, extend, unique, and vocab folders all contain SCORM-related XSD files. None of these files or folders should be edited, renamed, moved or deleted. You must keep them, but otherwise it’s best to act as if they don’t exist.
  • The imsmanifest.xml file is required for SCORM 2004. Don’t move or rename it. Some portions of the file may be edited, but you’d better know what you’re doing!
  • The SWF file (in my case sample.swf) is the Captivate course.
  • The HTM file (in my case sample.htm) is the HTML file that contains the SWF.
  • standard.js is a JavaScript file that contains some 3rd-party JavaScript utilities.
  • The SCORM_support folder contains files that Captivate uses to communicate with the LMS when using really old browsers. The files aren’t part of the SCORM standard, they’re just helper files created by Adobe.

Hopefully the files don’t seem so intimidating now that you know what’s what.  Once you ignore the XSD and DTD files, there are actually very few files left to work with.

The next post in this series will cover the HTML file produced by Captivate. Continue to Part 2.

Using the object element to dynamically embed Flash SWFs in Internet Explorer

This is a journey into the madness of Internet Explorer.

Yes, there is a happy ending. Jump to the end of the post if you just want the solution and don’t care about how we got there.

The Scenario

You want to embed a Flash SWF into your HTML document using the object element, and you need to be able to do it using JavaScript. (Let’s pretend SWFObject doesn’t exist, ok?)

If you were using HTML markup without JavaScript, embedding a Flash SWF using an object element would be pretty straightforward:


<object id="mySWF" width="550" height="400" 
        data="mymovie.swf" 
        type="application/x-shockwave-flash">
   <param name="flashvars" value="dog=woof&cat=meow" />
</object>

Microsoft designed Internet Explorer’s object to work a bit differently, so the markup for Internet Explorer becomes:


<object id="mySWF" width="550" height="400"
        classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
   <param name="movie" value="mymovie.swf" />
   <param name="flashvars" value="dog=woof&cat=meow" />
</object>

The key differences are:

  • Internet Explorer requires the classid attribute instead of type
  • Internet Explorer requires the param name="movie" child node instead of the data attribute in the object

Annoying, perhaps, but workable. If you’re working with hard-coded markup — what SWFObject refers to as static publishing — you can drop in some conditional comments and be done:


<!--[if IE]>
<object id="mySWF" width="550" height="400"
        classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
   <param name="movie" value="mymovie.swf">
<![endif]-->

<!--[if !IE]>-->
<object id="mySWF" width="550" height="400"
        data="mymovie.swf"
        type="application/x-shockwave-flash">
<!--<![endif]-->

   <param name="flashvars" value="dog=woof&cat=meow" />

<p>Fallback content for people without Flash Player</p>

</object>

That’s nice, but I need to use JavaScript

On the surface, using JavaScript to recreate the HTML markup appears to be a trivial task; we just need to add conditional logic to handle the two small differences between Internet Explorer’s object element and the object element used by the other browsers:


var target_element = document.getElementById("replaceMe"),
   obj = document.createElement("object"),
   isMSIE = /*@cc_on!@*/false;

//Add attributes to <object>
obj.setAttribute("id", "myObjID");
obj.setAttribute("width", "550");
obj.setAttribute("height", "400");

//Add <param> node(s) to <object>
var param_flashvars = document.createElement("param");
param_flashvars.setAttribute("name", "flashvars");
param_flashvars.setAttribute("value", "cat=meow&dog=woof");
obj.appendChild(param_flashvars);

if (isMSIE) {            

   //IE requires the 'classid' attribute
   obj.setAttribute("classid", "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000");
   
   //IE requires the 'movie' <param>
   var param_movie = document.createElement("param");
   param_movie.setAttribute("name", "movie");
   param_movie.setAttribute("value", "test.swf");
   obj.appendChild(param_movie);

} else {

   //Non-IE browsers require the 'type' attribute
   obj.setAttribute("type", "application/x-shockwave-flash");
   
   //Non-IE browsers require the 'data' attribute
   obj.setAttribute("data", "test.swf");

}

//Replace targeted DOM element with our new <object>
target_element.parentNode.replaceChild(obj, target_element);

Seems simple enough, right? Not so fast. If you try running this in Internet Explorer (6-9), you’ll notice the SWF fails to load, and IE will behave as though it’s stuck trying to load a file — the “loading” icon never goes away.

Issues with Microsoft’s proprietary classid attribute

It turns out Internet Explorer’s object does not like having the classid appended after the object has been created. This is very similar to the well-known IE bug for adding the name attribute to certain form elements. For the form element bug, Microsoft’s solution is to include the name attribute in the createElement string:

Attributes can be included with the sTag as long as the entire string is valid HTML. To include the NAME attribute at run time on objects created with the createElement method, use the sTag.

Microsoft provides the following example:


var newRadioButton = document.createElement("<INPUT TYPE='RADIO' NAME='RADIOTEST' VALUE='First Choice'>")

What happens if we use this solution for the classid issue?


var node_name = (isMSIE) ? "<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' />" : "object";
var obj = document.createElement(node_name);

This works like a charm… in IE 8 and lower. Unfortunately, IE 9 chucks a wobbly. Microsoft, in their wisdom, decided that angled brackets should no longer be valid inside the createElement method. This is actually excellent news: Internet Explorer 9 is behaving like other browsers, so let’s just use a try/catch to target IE versions prior to IE9, and use standard W3C code for IE9:


var target_element = document.getElementById("replaceMe"),
   isMSIE = /*@cc_on!@*/false,
   obj;

if (isMSIE) {

   try {
      
      //For IE 8 and lower
      obj = document.createElement("<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' />");

      //IE requires the 'movie' <param>
      var param_movie = document.createElement("param");
      param_movie.setAttribute("name", "movie");
      param_movie.setAttribute("value", "test.swf");
      obj.appendChild(param_movie);
      
   } catch (e) {
      
      //Let IE9 and higher fall through and use the standard browser markup
            
   }
   
}

if (!obj) {

   obj = document.createElement("object");
   obj.setAttribute("type", "application/x-shockwave-flash");
   obj.setAttribute("data", "test.swf");
   
}

//Add attributes to <object>
obj.setAttribute("id", "myObjID");
obj.setAttribute("width", "550");
obj.setAttribute("height", "400");

//Add <param> node(s) to <object>
var param_flashvars = document.createElement("param");
param_flashvars.setAttribute("name", "flashvars");
param_flashvars.setAttribute("value", "cat=meow&dog=woof");
obj.appendChild(param_flashvars);

//Replace targeted DOM element with our new <object>
target_element.parentNode.replaceChild(obj, target_element);

Still works in Internet Explorer 8 and lower, but fails in IE 9. Okay, perhaps IE9 still requires the classid attribute and ‘movie’ param. Let’s fork the code some more and try it out.


var target_element = document.getElementById("replaceMe"),
   isMSIE = /*@cc_on!@*/false,
   obj;

if (isMSIE) {

   try {
      
      //For IE 8 and lower
      obj = document.createElement("<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' />");
      
   } catch (e) {
      
      //IE9 doesn't support classid in createElement, so let's add it afterward
      obj = document.createElement("object");
      obj.setAttribute("classid", "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000");
      
   }

   //IE requires the 'movie' <param>
   var param_movie = document.createElement("param");
   param_movie.setAttribute("name", "movie");
   param_movie.setAttribute("value", "test.swf");
   obj.appendChild(param_movie);
   
} else {

   //Standard browsers
   obj = document.createElement("object");
   obj.setAttribute("type", "application/x-shockwave-flash");
   obj.setAttribute("data", "test.swf");
   
}

//Add attributes to <object>
obj.setAttribute("id", "myObjID");
obj.setAttribute("width", "550");
obj.setAttribute("height", "400");

//Add <param> node(s) to <object>
var param_flashvars = document.createElement("param");
param_flashvars.setAttribute("name", "flashvars");
param_flashvars.setAttribute("value", "cat=meow&dog=woof");
obj.appendChild(param_flashvars);

//Replace targeted DOM element with our new <object>
target_element.parentNode.replaceChild(obj, target_element);

Are you ready for the surprise? This doesn’t work in IE9, either. Adding classid to the object after it has been created simply will not work in any version of Internet Explorer. Microsoft removed their own recommended workaround — adding the attribute in the createElement string — to standardize their browser, yet they failed to provide full support for the related W3C standards being used by their competitors.

In their announcement of the createElement change for IE9, Microsoft recommended using the W3C standard setAttribute method, yet it fails for classid. *grumble*

To their credit, Microsoft also provided a second workaround consisting of strings and innerHTML:


var parent=document.createElement("div");
parent.innerHTML="<div id='myDiv'></div>";
var elm=parent.firstChild;

For our purposes, it would look like this:


function createIeObject(){
   var div = document.createElement("div");
   div.innerHTML = "<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'></object>";
   return div.firstChild;
}

var target_element = document.getElementById("replaceMe"),
   isMSIE = /*@cc_on!@*/false,
   obj = (isMSIE) ? createIeObject() : document.createElement("object");

if (isMSIE) {
   //IE requires the 'movie' <param>
   var param_movie = document.createElement("param");
   param_movie.setAttribute("name", "movie");
   param_movie.setAttribute("value", "test.swf");
   obj.appendChild(param_movie);
} else {
   obj.setAttribute("type", "application/x-shockwave-flash");
   obj.setAttribute("data", "test.swf");
}

//Add attributes to <object>
obj.setAttribute("id", "myObjID");
obj.setAttribute("width", "550");
obj.setAttribute("height", "400");

//Add <param> node(s) to <object>
var param_flashvars = document.createElement("param");
param_flashvars.setAttribute("name", "flashvars");
param_flashvars.setAttribute("value", "cat=meow&dog=woof");
obj.appendChild(param_flashvars);

//Replace targeted DOM element with our new <object>
target_element.parentNode.replaceChild(obj, target_element);

This feels like it’s getting us somewhere. Unfortunately, the SWF still doesn’t load. What else can we try? What if the ‘movie’ param were added at the same time the object is created and classid attribute is specified in Internet Explorer?


function createIeObject(url){
   var div = document.createElement("div");
   div.innerHTML = "<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'><param name='movie' value='" +url + "'></object>";
   return div.firstChild;
}

var target_element = document.getElementById("replaceMe"),
   isMSIE = /*@cc_on!@*/false,
   obj = (isMSIE) ? createIeObject("test.swf") : document.createElement("object");

if (!isMSIE) {
   obj.setAttribute("type", "application/x-shockwave-flash");
   obj.setAttribute("data", "test.swf");
}

//Add attributes to <object>
obj.setAttribute("id", "myObjID");
obj.setAttribute("width", "550");
obj.setAttribute("height", "400");

//Add <param> node(s) to <object>
var param_flashvars = document.createElement("param");
param_flashvars.setAttribute("name", "flashvars");
param_flashvars.setAttribute("value", "cat=meow&dog=woof");
obj.appendChild(param_flashvars);

//Replace targeted DOM element with our new <object>
target_element.parentNode.replaceChild(obj, target_element);

EUREKA! The SWF now displays as it should in all versions of Internet Explorer, and IE no longer behaves as if it’s stuck trying to load something. The takeaway is that Internet Explorer’s proprietary classid attribute and ‘movie’ param need to be created together or all is lost.

Why not use innerHTML all the way?

Many developers will say: Why not use innerHTML for the whole thing? After all, SWFObject 2.0 through 2.2 uses string building and innerHTML for the entire object creation in Internet Explorer:


el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';

In SWFObject, the attributes (att) and parameters (par) are added to the string via string concatenation. This is functional, but means that a completely different workflow — one devoid of standard W3C techniques — is required for Internet Explorer versus all other browsers.

For example, if there are many param nodes to add to the object, they will have to be generated as a long string of params in IE, while the fork for the non-IE browsers will all use a createElement/appendChild combination. In my opinion, it would be best to keep the code simple and use the same code for handling attributes and parameters in all browsers. This is ideal for maintenance, security, and file size.

Using the proposed IE solution allows us to modify the object via W3C techniques — we can add parameters and attributes and they all function as expected. The only difference between IE and other browsers is the creation of the initial object, but once it’s set up, IE’s object behaves the same in all browsers.

The obligatory poke at Microsoft

This is a perfect example of why there is not much love for Internet Explorer (and by extension, Microsoft) in many web developer circles. A task that should be trivial is utterly complicated, and requires hours of troubleshooting and experimentation just to achieve some basic functionality.

It’s my sincere hope that Internet Explorer 10+ will use the standard object code, but it probably won’t, since IE will probably still rely on ActiveX for the Flash Plugin. Falling short of using the standard object element, I hope that IE 10+ will at least allow us to add the classid attribute using setAttribute, and also allow us to add the movie param using createElement and appendChild.

SOAP for SCORM

At DevLearn 2010, Ben Clark and I presented a session named SOAP for SCORM on behalf of LETSI. The topic was the LETSI Run-Time Web Service (RTWS), a proposed modification of SCORM to use SOAP for communication rather than the current JavaScript model. I presented the first half of the session, covering the basic “what” and “why” ideas while Ben covered the technical “how” details in the second half.

This blog post is my attempt at documenting my portion of the session. Bear in mind that there was a lot of Q&A from the audience, and much of the session was ad-libbed. The descriptive text presented below may not exactly match was what in the session, but the gist is there.

If you’re interested in the nitty-gritty technical details, check out the Developer’s Guide on the LETSI website.

The Presentation

SOAP for SCORM: Linking Training Apps to Your LMS with LETSI's Run-time Web Service (RTWS)

 

Learning Education Training Systems Interoperability

Some quick background information on the LETSI organization.

LETSI stands for “Learning Education Training Systems Interoperability”.

LETSI believes in pushing training systems forward through agile, iterative software development. Make things quickly, get feedback, and make changes. In our current Internet age, we don’t have the time to take years developing a spec. Make something that works, then turn it into a spec.

LETSI believes in engagement with standards bodies and vendors throughout the process. If a new technology is to become mainstream, it’s vital to get vendors in on the action. Make them stakeholders and get them involved in the project at an early stage.

Unlike some standards bodies, LETSI is transparent and open — there are no strings attached. Membership is not limited to corporations or agencies. Want to have a say in the future of training technology? Join LETSI and you could be on the conference call next week.

 

What is the RTWS? An alternative to JavaScript

Simply stated, LETSI’s Run-time Web Service (RTWS) is a replacement for the SCORM JavaScript API. There are some other features that go beyond SCORM 2004, but the bulk of the RTWS is focused on replacing the JavaScript communication system with a SOAP system.

 

SCORM API: A JavaScript object in a web browser window

The current SCORM architecture requires that courses be launched in a frameset or pop-up window.

 

Course pop-up window communicates with LMS browser window via JavaScript

The course frameset/window contains a JavaScript-based API that communicates with the parent frame/window (the LMS website).

 

LMS browser window communicates with LMS server/database via JavaScript and server-side scripting

The LMS website typically performs data gets/sets on a database using a combination of ajax (xmlhttprequest) and server-side scripting (PHP, ASP, Java, etc.).

 

LETSI Run-time Web Service: A direct connection between the course and the LMS via SOAP (XML)

The LETSI RTWS changes the architecture to eliminate the middleman (the LMS website), allowing the course to directly communicate with the LMS backend using SOAP. (SOAP can be loosely described as XML sent via HTTP requests.)

 

What does this mean? (and why should I care?)

 

No more dependence on Web browsers

Replacing the JavaScript API with a SOAP system means the course no longer needs to be launched from the LMS. If the course is no longer required to be launched from an LMS, the web browser itself becomes optional, enabling the course to be launched from a variety of environments, including mobile apps, desktop apps, hybrid Web/desktop apps such as Fluid, Prism and Adobe Air, and other software solutions such as virtual worlds and serious games. This effectively makes SCORM’s CMI data model available to almost every software/web-based training solution.

No dependence on the web browser also means the course content can be hosted anywhere, and is no longer required to be stored in the LMS system.

Note: In some prototypes, the course must be launched from the LMS for the first attempt. This is due to registration requirements that are still being ironed out.

 

Better security

The JavaScript API has long been the weakest link in terms of security; it’s easy to hack and can’t be encrypted. Replacing the JavaScript API with SOAP (XML served via HTTP/HTTPS) enables courses to use discreet, secure communication via HTTP POSTs, similar to banks and other sensitive systems. RTWS courses will be protected via a combination of a shared key (a hash stored in both the LMS and the course’s code), user name, user ID, and a secure password incorporating SHA-1 encryption.

IMPORTANT: Since the shared key and the password are contained in the course’s code, the course will only be secure if the developer keeps it secure. For example, using JavaScript to handle your SOAP calls will make your data viewable to the client. If you keep all of the SOAP work behind the scenes using server-side code, your course will be much more secure.

 

Better accessibility

The accessibility benefits of the LETSI RTWS are debatable, but here are some of my personal beliefs about how the RTWS can make a course more accessible:

1. Since the RTWS discards the SCORM JavaScript API, a web-based course can be built to run without without a single line of JavaScript (traditional form POSTS and server-side code can process the SOAP requests). I’m not saying JavaScript is bad, but it’s a fair assumption that in most cases pages that don’t require JavaScript are more compatible with alternative browsing devices.

Also, as a developer, I know first-hand that when JavaScript is *required* for a course, many developers feel they’ve been given carte blanche to go hog-wild with JavaScript; for example, they might use it for interactive widgets that require a mouse (not keyboard accessible) and dynamically update/generate page content using ajax (difficult for screen readers and braille displays). These situations are problematic for people who use assistive technology; by eliminating SCORM’s JavaScript requirement, we can force developers to take a closer look at why and how they’re using JavaScript, hopefully leading to a more accessible course.

2. No reliance on web browsers means a course can be built in a desktop app or other environment that may (or may not) be more accessible than a web site or Flash movie. Of course, this is completely up to the developer, but the possibilities are there if the developer chooses to go down that path.

 

Less restrictive development environment

Under the LETSI RTWS, courses no longer need to be uploaded to the LMS and can be stored on ANY web server. There are several useful byproducts of this:

1. Course manifests no longer need to list every course asset. (Manifests are still required because they contain core data about the course, including the shared secret.)

2. If you’re a course vendor, you can maintain control of all of your course materials and simply provide a link and manifest to clients. No more ZIPs!

3. Since the course materials remain on your server and are not required to run on the LMS server, you’re free to use server-side code (previously a taboo in SCORM).

 

What’s the catch?

 

Requires more work

Despite their often negative reputations, LMSs provide a key service for your SCORM course: a SCORM API with error-checking (including error/debug messages) and behind-the-scenes handling of the CMI data model for you. The RTWS is not currently equipped to provide detailed error messages regarding the use of the CMI data model; if you choose to use the LETSI RTWS in place of the SCORM API, you will no longer have the benefit of the SCORM API’s error messages.

In the RTWS each ‘set’ request is an XML document created by your course and submitted to the server via HTTP. Since your course is creating and submitting the XML, it must use its own logic to determine if there are any errors, such as malformed XML or misuses of the CMI data model. For example, if your course accidentally tries to set a read-only field such as learner_name, you will not receive any error messages. It is up to your course to ensure the specifications of the CMI data model are being followed.

(Hopefully in the near future there will be 3rd-party libraries or frameworks that will provide this logic for you so you won’t have to write your own.)

Also, your course must be using a technology that supports SOAP.

 

Only supports single-SCO courses

The RTWS currently only supports single-SCO courses, and does not support SCORM 2004’s Sequencing and Navigation rules.

On a related note, since the content is being abstracted from the LMS, the LMS’s SCORM “player” will not be available to the RTWS course.

 

http://letsi.org/rtws

You can read more about the RTWS at http://letsi.org/rtws

Reminder: LETSI membership is open to the public. If you’d like help shape the RTWS (or other LETSI projects), join us!

Additional notes and tidbits not covered in my portion of the presentation

  • I briefly demonstrated a prototype I created for Flash-based courses. My prototype uses a modified version of my pipwerks SCORM ActionScript wrapper, allowing developers to simply swap out the pipwerks SCORM class file with a new pipwerks LETSI RTWS class file and still have their course behave as it did with SCORM 2004. This prototype is not available to the public yet, but when I’ve worked out the bugs I will be sure to post a blog entry about it. Stay tuned!
  • The LETSI RTWS is based on the SCORM 2004 (4th Edition) spec and uses the same CMI data model.
  • Because the RTWS uses a new launch approach, support for querying the course’s attempt history has been added to the data model (see GetAttempt and GetAttemptList in the RTWS documentation).
  • Unlike SCORM, a Get request in the RTWS returns the entire data set in an XML file. It’s up to the course to parse the XML and extract the value for a particular CMI element. I’ve whipped up a document mapping the CMI elements to their XML equivalents.
  • A Set request can be partial (set a single or small group of elements) or complete (set the entire data model).
  • SCORM’s Initialize() and Terminate() methods are not currently used in the RTWS.
  • Rustici Software (scorm.com), Booz Allen Hamilton, and Meridian are all creating LMS prototypes that support the RTWS.
  • Rustici Software hopes to fold support for the RTWS into its SCORM Cloud service once the RTWS is ready for prime-time.
  • The ADL (owners of SCORM) are not involved with the RTWS, but they are aware of its existence and are monitoring the situation. In a separate action, the ADL is requesting input on the future of SCORM via Project Tin Can. It is LETSI’s hope that the RTWS, when ready, will be folded into the ADL’s plans for SCORM and that the two will co-exist peacefully.

Dear Apple and Adobe

 

Update: Steve Jobs Responds! Well, not to my letter directly, but it hits on the major points and is a well-written explanation of Apple’s position.

Dear Apple and Adobe

I’m a long-time customer and have spent more money on your products than I have on just about any other aspect of my life. I’ve spent more money on your products than I’ve spent on my healthcare, vacations, kitchen appliances, children’s school supplies, or home entertainment system.

In return, you’ve increasingly shown a disregard for my needs and concerns, and have acted in ways that demonstrate all you want from me is my money.

For example, both of you have constantly forced me (or at a minimum pressured me) to buy updates to products I already paid for. For years I went along with it because I bought into the sales hype and assumed these updates would somehow make my life better.  In most cases, they did not.

Adobe, your constant tinkering with the Creative Suite has brought a few nifty tools to the world, but these new tools will not get me to overlook the incredible bloat you’ve unleashed on my computers — almost 6GB of program files on my Windows PC at work, and over 7GB of app files on my Mac at home. Your applications feel more unstable with every release, and your UI feels slow and unresponsive despite the extra RAM and other hardware upgrades on my machines. Some of the biggest security holes on my computers are due to your Acrobat software — the very same Acrobat software I’ve learned to hate because of how bloated, complicated, and unfriendly it has become. It feels like it gets worse with each release.

Apple, your innovation is refreshing. Adobe could learn a thing or two by examining your software: increased productivity through reduced feature sets and cleaner UI. Simple is usually best. However, despite your continued excellence in design, your behavior is repulsive. You’ve consistently screwed your early adoptors via your pricing schemes and forced millions of Americans to use a phone network they detest. (Why? Because AT&T was willing to give you a bigger cut of the revenue?) Worst of all, the totalitarianism displayed in your latest iPhone developer agreement is breathtaking. It appears your goal is to piss off everyone, even your staunchest allies… like Adobe.

Apple and Adobe, you used to play well together. You both benefited from your long-term relationship and grew into very large, very successful companies. I sincerely doubt either of you would have survived the 1990s intact if it weren’t for your partnership. Desktop publishing was the Mac’s forte and the one thing that kept it afloat when the buzzards were circling. And who provided the most popular DTP software? Adobe (and the companies Adobe acquired, like Aldus and Macromedia).

Adobe, I know you’re mad because Apple won’t let you put your Flash technology on the new iPhone platform (iPhone, iPod, iPad). Honestly, if I were controlling a platform, I would have major concerns, too. As I mentioned earlier, your track record for software quality seems to be in a steady decline. Your products have become infamous for security holes, bloat, and crashing. It didn’t used to be that way. Somewhere along the line you dropped the ball, and now it’s coming back to bite you. The good news is that it isn’t too late for you to reign things in and regain control of your software. Stop trying to please everyone by adding every conceivable feature under the sun, and really focus on the most important elements. Drop the cruft. Clean the cupboards. Get that lint out of your bellybutton. Once your software is respectable again, you’ll be in a much stronger position to complain about Apple.

Apple, I don’t know what happened to you. You went from being a popular underdog to being the class bully. You’re in danger of becoming as popular as Microsoft in the European court system. From where I sit, your biggest mistake has been the idea that you can take over the world, one industry at a time. Of course, many companies are aggressive and set big goals for themselves, but they don’t stab their partners in the back as quickly and viciously as you seem to do. Your hubris and eagerness to expand into your partners’ markets is going to be your downfall. People have liked you because of your design sensibilities and because you were the hip underdog. You are no longer the hip underdog, and with time, other companies will create products that will be (almost) as stylish but also cheaper and with equivalent or greater capabilities.

The bottom line is that neither of you are choir boys, and I’m fed up with your bickering.

Adobe, stop playing the sympathy card. It’s a complete turn-off because I know how crappy your software can be. Granted, it’s unfortunate that so many people depend on Flash and Flash doesn’t work on the iPhone platform, but Flash is not a web standard. For all its shortcomings, the iPhone platform has one excellent quality: a top-notch HTML5 browser. Standardistas have been warning people not to go all-in with Flash for years, and now we see why. If it isn’t part of a standard, it will not be incorporated into some products. It’s the vendor’s choice. Simple as that.

Apple, stop trying to take over the world. We’ve seen what happens to other companies who try it, and it never looks pretty. Focus on your core values and let your partners do their thing without stepping on their toes.

Oh, and ditch AT&T already, will ya?

Respectfully,

Philip

Speaking of IMS…

IMS produces standards they want everyone to use.

Why, then, do I have to log in to their site in order to VIEW the documentation for their standards?

Situation: I found a link to a PDF on their site. When I clicked it, I was presented with this warm greeting [link no longer available]:

Sorry
The page you are trying to access is reserved for participants in the IMS GLC Public Community or is reserved for IMS GLC Members, Affiliates or Alliance Members.

If you have already set up an account and just need to login, please do so here. If you would like to register for a free public community account, just head on over to our registration page.

So… in order for me to even read their standards, I have to tell them who I am and put my personal information in their database? How does this foster adoption of standards?

Geez these guys burn my britches…

UPDATE:

The IMS site requires the following information before you can view any of their documentation: Name, email, organization name, job title, job description, country

Pretty invasive if you ask me.

UPDATE #2:

After registering for site access, I was greeted with a second page asking for persona information, and asking me to agree to IMS’s licensing terms. They require the following information: Name, email, “entity on whose behalf you are accepting this agreement”, street address (includes city, zip, country), phone number

(Phone number? Seriously?)

The license terms are FIVE PAGES long after pasting into a Word file.

Interesting bits (emphasis mine):

IMS specifications are published solely for the purpose of enabling interoperability among learning products and services used by the education and training communities served by IMS members and are made available under license to Registered Users solely to further that purpose.

I guess this means the standards aren’t meant for public consumption after all, though the end result of the standards are?

Users of the Specification(s) are encouraged to engage directly with IMS as IMS members, including registration of all applications of specifications, in order to enhance the level of interoperability in the education and training industries worldwide.

I read this as: tell us who you are and how you’re using IMS specifications so we can include you in our next press release (you know, the part where we pat ourselves on the back).

Any use of the Specifications(s) or other Materials provided by IMS must be accompanied by the copyright legends provided with full attribution given to IMS.

If this were the case with the HTML, ECMAScript, or XML standards, our documents would be bloated with useless attribution credits. (Side note: I wonder how this affects SCORM, as SCORM uses IMS specifications for packaging? Is anyone who produces a SCORM-based course supposed to pay respect to the Don provide attribution to IMS in their courseware?)

Licensee agrees to publicly and visibly acknowledge and attribute to IMS the Specification(s) upon which products are based to any and all Development Partner(s).

So if Company X uses an IMS specification, they’re supposed to go over to each “Development Partner” — many of whom may be commercial competitors — and let them know?

IMS issues press release for new e-learning interoperability standards

News from the IMS Global Learning Consortium:

The IMS Global Learning Consortium (IMS GLC) today announced the public review of the first phase of the Learning Tools Interoperability (LTI) standards to allow open and seamless integration of educational web applications. Called “Basic LTI or BLTI,” this first standard addresses the most common roadblocks to achieving a seamless experience for teachers and students.

Reading this press release, I couldn’t help but notice two things:

  1. There’s an awful lot of chest-puffing and self-congratulation going on here — people’s credentials are thrown around an awful lot, and over half of the press release is filled “Statements of Support.”
  2. There’s no hyperlink to the standards that are being announced, nor is there any information about how you can read the new standard proposal, whether you can try it yourself, how long it’s open for review, etc.

If the new standards are written as poorly as this press release, it’s going to be 1,000 pages of useless spec. All filler, no killer.

Best Practices in E-Learning

Someone recently posted a blog entry ranting about the use of the term “best practices” in our industry. I understand the frustration with thoughtless pronouncements about best practices, especially coming from people who may not know any better; it will often sound a lot like how mom used to say “eat this, it’s good for you” without really knowing whether it’s true. However, there is a big difference between best practices in terms of learning theory — something that’s difficult to quantify/prove — and technology.

A friend of mine, upon completing his MA in psychology, joked that his degree is the only one you can get where you can’t prove a thing that you’re taught. Learning theory is a form of psychology, and as such, you are guaranteed to run into a gazillion different opinions on how learning occurs: behaviorism, constructivism, cognitivism, yadda yadda yadda. Likewise, you will hear many opinions on what development models to follow (ADDIE vs agile vs something-or-other), evaluation methodology, and perhaps edge-case debates such as centralized learning structures versus de-centralized learning structures (social media peeps, I’m looking at you).

I guarantee these conversations will involve lots of name-dropping and liberal use of the term “best practice.” In these situations, I agree that there is no single answer to ANY of these issues, and context will be king.

Most technical issues, on the other hand, most certainly DO have best practices, and for good reason.

For starters, accessibility is a best practice. Why? Well, because it’s the right thing to do. No one should be denied an opportunity to learn simply because their ears or eyes or arms don’t work like yours do. Establishing a baseline level of accessibility is fairly easy to do, regardless of the size of your budget or your time constraints. For example:

  • For the hearing impaired: Text transcriptions and/or closed captioning for videos and Flash animations are as easy to set up as ever. Free/cheap video players like the Flash video component, the JW Player, and Flowplayer all support multiple captioning standards and make it easy to add captioning to a video. Rapid e-learning development tools such as Articulate Presenter and Adobe Captivate allow you to add captions or notes to your SWFs. (Side note: the text transcriptions for TED talks are an excellent example of what can be accomplished with just a little extra effort.)
  • For the visually impaired: If the content of your course is provided in a text format such as HTML, screen readers can read the text to the end user. What does this require of you? Well, if you use standard HTML, not much… just a little extra care in your layout and alternate text. If you embed an image, video, or animation, provide fallback text that describes the image or what happens in the video/animation. SWFObject (a free system for embedding SWF files in HTML documents) makes this easy to do.
    Similarly, Adobe has been working hard to make Flash Player and Adobe Reader more accessible to major screen readers. What do you have to configure to make it work? Nothing so far.
  • For those who can’t use a computer mouse: Thanks to initiatives like WAI-ARIA and companies like Adobe who are actively building keyboard support into their products, many script-based interactions (such as course navigation, quiz questions, and other activities) can be scripted to work without a mouse. Alternate input devices are often mapped to the keyboard input; if your course can be completed using a keyboard, you’re golden.
  • For the color blind: Accessibility can often be improved simply by adding text labels to color-coded objects and not relying on color alone.

I could go on for a while, but the point is that accessibility is definitely a best practice. It isn’t hard, and it certainly isn’t expensive to make a course accessible. It’s also the law if you receive any Federal funding.

There are definitely other technical best practices for e-learning:

  • SCORM: Technically not a standard but rather a collection of standards, SCORM is a best practice because it ensures your course will work on pretty much every major LMS (if you don’t like SCORM, AICC is equally valid). How can I say with confidence that SCORM is a best practice? Because in the bad old days before SCORM, developers had to spend weeks re-coding courses to work with each LMS’s proprietary code base. Once SCORM was widely adopted, the issue largely went away. No one wants to go back to the bad old days.
  • Valid HTML and CSS: If you write HTML and CSS, ensuring they validate means you know your pages will work in every major browser. We learned this lesson in the Netscape/Internet Explorer wars. Best practices on the web are still evolving; for example, sometimes it’s ok to write CSS that won’t validate if you know the repercussions and your code fails gracefully in older browsers. The best practice is simply that your pages work in most, if not all, browsers.
  • Don’t use proprietary code: See above. If your course uses ActiveX, which is only supported in Internet Explorer, your course won’t work in any other browsers. Almost anything implemented with ActiveX can be implemented using other non-proprietary methods. Again, the best practice is to ensure your pages work in most, if not all, browsers.
  • Follow sensible coding conventions: Well-written code that follows documented — and very well-reasoned — code conventions means your code will likely contain less errors, will be easier to update if the need arises, and will be more future-proof, avoiding expensive bugs like the Y2K bug, which could have been prevented with a bit of foresight. A great example of this type of code convention is Douglas Crockford’s JavaScript: The Good Parts.

There are definitely times when people throw around the phrase “best practice” and are simply talking out of their butts. “Never use yellow.” “Never use clip art.” “Never hire a penguin.” “Never let the learner do X.” “Always make the user do Y.” “Always use ___ format.” “Always use ___ pedagogy.”

Whatever.

Just remember that best practices DO exist, but not in every circumstance. And unless you want the evil eye from me and my compadres, remember to never use the phrase “best practice” unless you can back it up with evidence and sound reasoning.

Post script: I’ve noticed this bandying of best practices usually occurs when someone is trying to establish their expertise or exert control on a project, frequently in front of management-types. This is the same sort of thing Machiavelli did when he wrote The Prince, so why not treat them as Machiavelli would and … well, I guess another best practice is to know when to shut up, so I’ll stop here.