Using ExternalInterface to write Javascript to the HTML DOM
December 12th, 2007
Author: Michael Krotscheck
Category: Tutorials
Tags: actionscript, javascript
Every Actionscript Developer will sooner or later have to interface with the webpage in which his .swf is presented, and Adobe has provided the ExternalInterface Class to simplify this process. Even so, communicating with an external javascript API almost inevitably requires some number of bridging functions to properly couple the two runtime environments. In cases where the developer has control over both environments this poses no further problems, as for example in an application where the .swf takes animation and event cues from an interface written largely in HTML. Some instances nevertheless remain where we don’t have that luxury, and are writing to an external API that we can neither guarantee nor contribute to; In short, we cannot add a javascript file to the HTML wrapper that will act as our translation bridge.
This situation is particularly important for individuals writing packages to simplify interaction with common public interfaces such as Omniture, WebTrends, or other Web 2.0 API’s. When developing for these, we’re left with the chore of packaging a javascript file with our .swc as well as extensive documentation on how to properly integrate the pieces. Yet it turns out that this rather messy solution is not necessary at all, because of a peculiar behavior of ExternalInterface.call(): Rather than execute the method of the name that you pass, it attempts to resolve whatever you pass to it as a function. The following example demonstrates this:
import flash.external.ExternalInterface;
// This line passes a string to the alert function within the javascript environment.
ExternalInterface.call("alert","foo");
// This line creates a function that sets a variable and then calls alert on it.
ExternalInterface.call("function() { alertString = 'foo'; alert(alertString); }");
// Note that they both do exactly the same thing.
Since javascript is poorly scoped, this gives us access to set or retrieve anything within the HTML DOM. In short, we can write our own custom javascript directly to the page without having to rely on an external file. Security concerns aside (imagine covertly embedding Facebook’s beacon into a banner ad), the usefulness of this technique should be obvious. Even so, manually writing every javascript function into a method call is no way to keep your code legible, and since actionscript does not allow us to include text via embed directives, so writing external .js files isn’t an option. Instead, the only real option is to define a series of strings, or to make use of E4X to include our javascript in CDATA tags as follows:
<mx:xml id="scriptCache">
<scripts>
<script methodcall="demoFunction" methodname="jsDemoFunction">
<![CDATA[
function(var1, var2, var3) {
alert(var1);
}
]]>
</script>
</scripts>
</mx:xml>
At this point we have a solution to our problem: We can embed our API into our actionscript class, easily edit it within the XML, and write it to the HTML DOM when it is necessary. The last piece of the puzzle then is to ensure any library we write is entirely transparent to other actionscript developers. In short, no method call should visibly use ExternalInterface. This is where mx.flash.Proxy comes in handy, because it allows us to pass arbitrary function calls to a handler of our choice. The implementation thereof is beyond the scope of this article, though you should have enough pointers to write your own. Even so I’ve taken the time to write a class that doesn’t just allow the registration of methods, but also gives you easy access to all the javascript variables. You can download it and the documentation here.

No comments yet.