Your web browser knows a lot about you, and that information is available to any website you visit. And since our e-learning projects are just webpages, it’s available to us too! Don’t worry, I’m not talking about the creepy stuff like when you get a Facebook ad for that niche thing you were just talking to a friend about.

Consider this scenario: You’re making a training module on a system and the user interface of that system varies based on the computer, i.e. it looks different on Windows vs a Mac. Or maybe it’s responsive and has a unique interface when accessed on a smartphone. Or perhaps there’s a known bug you need to address that only appears in Google Chrome.

In all these cases you might create branches and let the user manually pick, but wouldn’t it be cool to do it automatically? Well, you can!

(Psst, hey, want to skip all the explainy bits and go straight to the e-learning part? Click here!)

Meet User Agent!

Your browser automatically makes available a bit of text called the User Agent. It contains info about the operating system and the browser itself. Any website can easily grab this info and do something with it. For example, I shall now do so!

Your User Agent is:

Not Found!!

Which means…

  • Your operating system is: Unknown!
  • Your browser is: Unknown!

Magic? Nope, just JavaScript! Getting the User Agent is a simple task, plenty of documentation out there about it, just a simple var userAgent = navigator.userAgent; and you’ve got it. The harder part is parsing it down into usable bits.

Parsing User Agent (The Hard Way)

When I first started messing with this idea I was trying to do stuff like this, a bunch of guesswork based on possible strings:

var userAgent = navigator.userAgent;

var os = "Unknown OS";

if (userAgent.indexOf("Win") !== -1) {
     var os = "Windows";
  } else if (userAgent.indexOf("Android") !== -1) {
     var os = "Android";
  } else if (userAgent.indexOf("like Mac") !== -1) {
     var os = "iOS";
  } else if (userAgent.indexOf("Mac") !== -1) {
     var os = "MacOS";
  } else if (userAgent.indexOf("X11") !== -1) {
     var os = "UNIX";
  } else if (userAgent.indexOf("Linux") !== -1) {
      var os = "Linux";
}

Ew. Don’t do that. See, the problem is there are thousands of possibilities, and they change from time to time. So even though it’s hypothetically simple to do something like the above, I suggest letting someone else do the hard work.

Parsing User Agent (The Easy Way)

Lucky for us, this exists: UAParser.js. This is a super easy-to-use library that is backed by a huge database of possible User Agent strings. It can be called via CDN, making it easy to use in Captivate or Storyline (more on that in a moment), and abstracts away all the complexity to give us a much more reliable result!

const uap = new UAParser();
var os = uap.getOS().name;

Much easier! But before we get to the e-learning part, a quick word of warning…

But…

The fact is that even with our helpful library to do the hard work, the User Agent can’t be 100% relied on for a few reasons. For one, it can be changed or obfuscated, it’s not common but some privacy minded users may do so. Also, like mentioned above, the browser makers can change how it’s formatted and even with that library to help it might not be updated fast enough. There’s also the possibility that the script fails for some reason or is blocked by a firewall etc. For all those reasons I strongly suggest you use this feature in conjunction with a manual select option for the user. For example:

This lets the user correct an error. Plus, maybe they happen to be taking the course on a Mac but will actually be using the system primarily on Windows! This way you are supporting user choice, but still get the wow factor of the automatic adaptation!

Let’s Apply It!

I recently dusted off an old course on Excel Basics and updated it to experiment with this idea. Honestly it’s not the best use case since Excel is pretty much identical between Windows and Mac these days, but it still works as a demonstration. There are some minor differences in the UI that could throw off a brand new user, so this way they see screenshots that exactly match their environment.

The basic steps are:

  1. Add custom JavaScript to your course that gets the user agent, parses it, and saves the relevant info (in this example, the OS) into a variable.
  2. Anywhere in the course there’s a variation, i.e. every screenshot, include all applicable variations (in this example a screenshot from Windows and one from a Mac).
  3. Use Actions or Triggers to hide the irrelevant stuff and show the applicable versions based on the variable.

That’s it, that’s the whole idea! But let’s break it down shall we? Oh, and this course was originally Captivate 9 (I think? Now it’s Captivate Classic), but all of these concepts work in Storyline too, so I’ll cover both.

The JavaScript

There are arguments to be made about about how and where to add custom JavaScript to courses, but I’m not going to get into all that. My simple approach here is to just have one of the first slides execute a script on entry.

In Captivate, here’s the code I’m using (only the last line changes for Storyline, it’s down below):

function loadScript(src, callback) {
    var script = document.createElement('script'); 
    script.src = src;
    script.onload = callback;
    script.onerror = function() {
        console.error('Failed to load script: ' + src);
    };
    document.head.appendChild(script);
}

loadScript('https://cdn.jsdelivr.net/npm/ua-parser-js/dist/ua-parser.min.js', function() {
    const uap = new UAParser();
    var os = uap.getOS().name;
    window.cpAPIInterface.setVariableValue("userOS",os);
});

This is a template format I use anytime I want to use a library on a CDN, probably worth its own article now that I think of it… But for now, here’s a simplified explanation:

  • Lines 1-9 are a general function that adds a script tag to the page. That script tag will call the CDN.
  • Line 11 is activating that function and giving it the specific library we want to use, in this case UAParser.js.
  • Lines 12 & 13 are talking to UAParser.js, asking it to parse the user agent, and then saving that in a JavaScript variable.
  • Line 14 is the unique function for Captivate, it tells Captivate to store the JavaScript variable called os in a Captivate variable called userOS.
    • The equivalent code in Storyline is player.SetVar("userOS",os);

In my example I’m only using the OS, but way back in the opening paragraph I mentioned some other uses. For instance if you wanted to adapt your course based on the browser, a specific version, or whether it’s a phone, you can do that. Here are some example snippets you might add in with line 13 (just make sure you add matching versions of line 14 for each):

// To get the user's web browser:
var browser = uap.getBrowser().name;

// To get the specific version of the OS:
var osVersion = uap.getOS().version;

// To tell whether it's a mobile, tablet, or desktop is a bit more complicated:
var deviceType = uap.getDevice().type || "desktop";
// The extra bit is an 'or', since something not mobile or tablet just doesn't have a type at all.

Check the UAParser.js documentation for all the other possibilities. Or, if you can’t (or don’t want to) use that library, you can modify the “hard” example above with the help of the User Agent details here.

The Course Content

This part is either easy, or really complicated, depending on just how many variations you need. Basically you’re using an advanced action/trigger to do one of the following based on the variable set by the code above:

  • Hide one object (a screenshot, video, block of text, etc) and show another. This gets very messy but is sometimes necessary in Captivate when the other options won’t work (e.g. with a group of objects).
  • Switch states on an object where different states contain different variations. This is the cleaner way, but only works with single objects (not groups).
  • Show layers in Storyline. This is the cleanest “manual” way I think, but there’s not an equivalent in Captivate.
  • Jump to different slides or use branches. I only like this approach if there are big differences and all the content on the slide needs to be different. If it’s just differences in screenshots, shortcuts, etc (which is where this technique shines) then doing it this way will just be a lot of duplication. I don’t recommend it.
  • Hypothetically, run JavaScript to change all the impacted object names. This would be the cleanest, but I haven’t actually tried it yet – If anyone gets it working please let me know!

Let’s look at some examples of the first three.

Show/Hide Objects

On this slide I opted to group a screenshot and some callouts for each OS variation since they were very different shapes. You can see the 2 overlapping groups on the slide and in the timeline. Then there’s an Advanced Action triggered on slide entry that has conditional tabs for the variations (the Windows tab is the same actions, just reversed) that Hide the not-applicable group and show the applicable one. It works, but it’s inelegant and not ideal.

Oh, and this isn’t an approach you’ll need in Storyline since it has layers!

Switch States

Here I only needed to swap 1 screenshot, so editing the states where each state is a different one is ideal. Similar action but just one line in each tab.

In Storyline it looks like this, same concept. Note that Storyline will get a bit more complex to do this way if you will have more than 2 or 3 variations since triggers don’t support more than 1 else if. Layers are the better bet in that case.

Layers

This option is only available in Storyline since Adobe never bothered to add an equivalent feature to Captivate. Gee, I wonder why they fell so far behind? But that’s a different blog post…

Because layers are hidden by default, there’s no need for any if/then/else logic. You’ll need a trigger to show each variation, but I still think that’s a lot cleaner than the other approaches!

Conclusion

In summary:

  • User Agent is a string of info available to any web page (or e-learning!) that contains info about what OS and web browser the user is using.
  • The User Agent can be grabbed and parsed with JavaScript manually or using a helpful library.
  • Both Captivate and Storyline can then store the details in a variable that you can use to customize the content of your course as applicable.

Hopefully this has inspired you to try some new customizations in your courses by leveraging some (relatively) simple JavaScript and the power of the User Agent! If you apply this concept in any interesting ways please be sure to tell me about it, hit me up on your choice of socials or use my contact form!