Flite Careers

Using the ExternalInterface Class to Bridge JavaScript and Flash

At Flite the core of our Desktop Ad product is a SWF file that pulls in data and the structure of an ad in order to put it together on the client. In previous versions, the SWF file contained most of the code to interface with our servers. Recently, we built a Touch Ad product that was entirely HTML5, and with it a new JavaScript API was created to talk to our servers. Instead of maintaining two versions of the API code, we decided to go with a pure JavaScript implementation and used Flash’s native ExternalInterface class to call into the JavaScript API.

The ExternalInterface class is extremely handy, but troubleshooting your implementation can prove to be difficult. Here are some helpful tips for using the ExternalInterface class that I’ve picked up along the way.

Basic examples of using ExternalInterface

The most useful function of the ExternalInterface class is call. A typical use case is to execute existing JavaScript that might be on the page. But a more exciting use case is to execute your own JavaScript functions in ActionScript code. Anonymous functions are great for one time usage. For example, opening a predetermined URL:

1
ExternalInterface.call("function(){window.location.href = 'http://flite.com';}");

ExternalInterface.call also allows you to pass any number of parameters to JavaScript functions. The first argument is the function (passed as a String), followed by the parameters to be passed to the function. For example, you can get trace output on machines without the Flash Debugger version installed. ExternalInterface can be used to log into your browser’s JavaScript debugging console instead of the default location.

1
ExternalInterface.call("console.log", myVar, myVar2);

For times when you need to make repeated calls to the same function, improve your performance by using static constants.

1
2
private static const ALERT_FN:String = "function(msg){alert(msg);}";
ExternalInterface.call(ALERT_FN, "ding!");

As your code gets more complex, I suggest breaking it up onto several lines to make it readable.

1
2
3
private static const ALERT_FN:String = "function(msg){" +
    "alert(msg);" +
"}";

How to Debug your JavaScript

Make your code debuggable by adding “debugger;” statements. The newline characters can help with formatting of the code when the breakpoint catches in the debugging console. Most developer tools – such as Chrome’s Web Inspector – allow you to format JavaScript, so the newline characters may not be needed.

1
2
3
4
private static const ALERT_FN:String = "function(msg){\n" +
    "debugger;\n" +
    "alert(msg);\n" +
"}";

Improve performance and your ability to debug your code even further by injecting complex functions onto the page in an initialization function. By doing this, you can also execute your injected JavaScript functions directly in the browser’s debugging console. I recommend creating a namespace object on the window that is unique to your SWF, so that you don’t overwrite existing code in JavaScript.

1
2
3
4
5
6
7
8
9
10
11
private static const MYFUNC_FN:String = "window.MY_NAMESPACE.myFunc";
private static const INIT_JS:String = "window.MY_NAMESPACE = window.MY_NAMESPACE || {};" +
MYFUNC_FN + " = function(arg1, arg2){ /*do a lot of things*/}";

public function MyConstructor(){
     ExternalInterface.call(INIT_JS);
}

private function doMyFunc(p1:String, p2:Boolean) : void {
     ExternalInterface.call(MYFUNC_FN, p1, p2);
}

From JavaScript to ActionScript

Beyond just executing javascript, Flash can also evaluate return values coming back from JavaScript. You could use JavaScript to generate random numbers, for example:

1
2
3
private function getJSRandom() : Number {
     return Number(ExternalInterface.call("Math.random"));
}

Avoid passing objects that reference non-primitive types (i.e. DOM, Functions, etc). JavaScript is designed to work with the DOM object which is known to be full of circular references. For example, a DOM node has a property referencing its parent element and the parent also has references to its children. When passing values back and forth between Flash and JavaScript, the arguments and return values are serialized and the circular references will cause Flash to come crumbling to its knees.

For example, the first code block will not work, while the second one will:

1
2
var el:Object = Object(ExternalInterface.call("document.getElementById('container')");
trace(el.nodeName);
1
2
var nodeName:String = String(ExternalInterface.call("function(){return document.getElementById('container').nodeName;}");
trace(nodeName);

And finally, if the need arises, you can also call into your ActionScript code from JavaScript by using the ExternalInterface.addCallback function to register an ActionScript function. JavaScript can also evaluate return values returned from your ActionScript functions.

1
2
3
4
5
//AS3 Code
var doSomething:Function = function(arg1) : void{
    //do something here
}
ExternalInterface.registerCallback("myASFunc", doSomething);
1
2
3
4
5
//JS Code
var myObj = document.getElementById("my-object-tag");
if(myObj.myASFunc) {
   myObj.myASFunc("foo");
}

For more on Flash’s ExternalInterface class, see Adobe’s API documentation here.