Hack 94 Integrate the Back Button with Flash

figs/moderate.gif figs/hack94.gif

A long-standing complaint against Flash's usability is that it didn't work with the browser's Back button. Dispel this myth?use the browser Back button to navigate back in a non-timeline-based Flash site.

Part of the design intent of Flash MX (and Flash Player 6) was to make Flash more accessible. Among other things, Macromedia added support for named anchors.

Most designers know anchors by their HTML-based implementation. An anchor is a link within a document (usually) to another part of the same document. It is often used in long text documents in which significant parts of the document scroll outside the visible area of the browser window. Rather than having to scroll up and down the page, an anchor allows users to jump to the required section via an intrapage link. The anchor name is preceded by a #, such as http://www.oreilly.com/index.html#faq.

In its implementation of anchors, Macromedia equated the HTML document to the SWF's main timeline, so anchors allow you to jump back and forth along _root using the browser Back button (assuming an accessibility-enabled browser). Naturally, within Flash, you can still use typical Flash navigation (i.e., Flash buttons with gotoAndPlay( ) actions as part of their onRelease( ) event handlers).

One major problem is that most Flash sites do not use frames on the _root timeline as anchor points. When the user clicks on a button, he doesn't jump to a frame on _root because that approach makes the SWF difficult to maintain and has a number of issues when you start using bandwidth-heavy content. Instead, a Flash interface for a typical commercial application uses an ActionScript-based system that employs loadMovie( ) and nested movie clips or attaches content directly to the main timeline.

The only way to hack around this is to have hidden HTML pages that change in step with the Flash site; it is these hidden pages that populate the browser history. When the hidden HTML pages change due to the browser Back and Forward buttons, JavaScript is used to send variable values to the Flash site telling it about the page changes, and the Flash site is configured to catch and respond to these changes.

You can't use JavaScript to simply read the browser history and send it to Flash because it isn't accessible to JavaScript (or anything else) for security reasons (i.e., keeping your browsing preferences outside the reach of advertisers).

Creating the HTML

By far the easiest way to set up your hidden HTML pages is to include them in the same page as the Flash site using a frameset. Given that many Flash users have the Macromedia Studio MX package, we will use Dreamweaver MX (you can enter the HTML listings shown in an editor of your choice).

Dreamweaver MX has something close to the frameset we need as a standard template, so select FileNew and select the Fixed Bottom frameset as shown in Figure 11-19.

Figure 11-19. Using the Fixed Bottom frameset template in Dreamweaver MX
figs/flhk_1119.gif


Our Flash HTML file will populate the top frame, and the hidden HTML pages will be implemented as blank pages (containing only JavaScript) in the lower frame.

Using this template (or similar), create the following HTML. The lines I have changed from the Dreamweaver MX defaults?the title, the two frame source files, and the noframes content)?are highlighted in bold. Save the frameset as frame.html.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/

TR/html4/

frameset.dtd">

<html>

<head>

<title>:: Flash with Browser History ::</title>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">



</head>

<body bgcolor="#FFFFFF">

<frameset rows="*,80" frameborder="NO" border="0" framespacing="0">

  <frame src="flash.html" name="mainFrame">

  <frame src="one.html" name="bottomFrame" scrolling="NO" noresize>

</frameset>

<noframes><body>

You should really get a more up-to-date browser before they phase out steam power.

</body></noframes>

</html>

Next up, our hidden pages. Create five blank HTML files named one.html, two.html, three.html, four.html, and five.html, and save them in the same folder as frame.html.

Modify the first file, one.html, as shown (changes from Dreamweaver MX defaults are shown in bold):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>one</title>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

</head>

<body bgcolor="#FFFFFF">

  <script language='JavaScript'> 

    parent.mainFrame.flash.SetVariable('history', 1);

  </script>

</body>

</html>

Note that the <title> tag should change for each file, in this case one, and the line of JavaScript should specify the file number, in this case 1, as the second parameter passed to SetVariable( ). We explicitly set the background color of the bottom frame pages, so that they aren't noticeable if the user has a different default background color set.

Modify the remaining four HTML files similarly. For example, here are the changes to five.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>five</title>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

</head>

<body bgcolor="#FFFFFF">

  <script language='JavaScript'> 

    parent.mainFrame.flash.SetVariable('history', 5);

  </script>

</body>

</html>

Your frameset won't work just yet because it is missing the flash.html section, so let's create that next.

In Flash, create a new Flash Document with FileNew. Select FilePublish Settings and set these options under the HTML tab of the Publish Settings dialog box: set the Horizontal and Vertical options under Flash Alignment to Center, and set the Scale option to No Scale, as shown in Figure 11-8 [Hack 90].

Save the Flash file as flash.fla in the same folder as the previously saved HTML files (but remember to upload the SWF, not the FLA, if you decide to put the files online!). Publish the file (FilePublish) to create flash.html and flash.swf.

Creating the Flash Content

We will create a simple embedded timeline to show the principle of the hack.

Create a new movie clip symbol with five keyframes (it doesn't matter what the symbol is named), as shown in Figure 11-20, each of which shows a different page of our Flash application, as shown in Figure 11-21.

Figure 11-20. A clip with five keyframes representing different pages of our Flash application
figs/flhk_1120.gif


Figure 11-21. Pages of our Flash application
figs/flhk_1121.gif


Place the clip onto the Stage at frame 1 of the main timeline, and give it the instance name content using the Properties panel. Next, add a menu of five buttons with instance names b1 (the 1 button) to b5 (the 5 button).

Finally, create a new layer above the current one, calling it actions. Attach the following script to frame 1 of this new layer:

b1.onRelease = function( ) {

  fillBottom("one.html");

  content.gotoAndStop(1);

};

b2.onRelease = function( ) {

  fillBottom("two.html");

  content.gotoAndStop(2);

};

b3.onRelease = function( ) {

  fillBottom("three.html");

  content.gotoAndStop(3);

};

b4.onRelease = function( ) {

  fillBottom("four.html");

  content.gotoAndStop(4);

};

b5.onRelease = function( ) {

  fillBottom("five.html");

  content.gotoAndStop(5);

};

function setHistory( ) {

  if (history == 1) {

    hisEnabled = true;

    content.onEnterFrame = function( ) {

      if (history != 0) {

        this.gotoAndStop(history);

        history = 0;

      }

    };

  }

  clearInterval(startHistory);

}

function fillBottom(doc) {

  if (hisEnabled) {

    getURL(doc, "bottomFrame");

  }

}

var history;

var hisEnabled = false;

startHistory = setInterval(setHistory, 500);

content.stop( );

When you click each of the buttons, not only does Flash jump to the appropriate frame in the Flash timeline, but the function fillBottom( ) jumps to a corresponding HTML page in the lower frame of the final web page. Although the user doesn't see these HTML pages (they contain no visible content), the browser adds them to the browser history.

As the user clicks the browser Back and Forward buttons, the same HTML pages are reloaded from the browser history, and the JavaScript inside them changes a Flash variable, history, to a value between 1 and 5, depending on the page.

The onEnterFrame( ) handler catches this change and forces content to change its appearance in sync with the HTML page change, thus propagating the browser history to the Flash movie.

Note that when you are using the Flash buttons for navigation, you actually tell the movie clip content to jump to the required frame twice, once from ActionScript and once from JavaScript. Rather than remove the ActionScript jumps, it's better to leave them in for redundancy; this ensures that the navigation still works, even if JavaScript isn't available.

The ActionScript is written so that:

  • The navigation still works if JavaScript is not available in the current browser (or the SWF is running in a Standalone Player). Flash knows if JavaScript is not working via the if (history==1) check within setHistory( ). If JavaScript is disabled, history is undefined, and Flash addresses this by not setting up the onEnterFrame( ) handler so that no checks on browser history are made.

  • A delay of 0.5 seconds is included before ActionScript starts talking to JavaScript. The HTML doesn't seem to start setting the Flash variable history until after a short delay. The ActionScript therefore delays the creation of the onEnterFrame( ) handler for 0.5 seconds using setInterval( ).

Final Thoughts

If you set up a history by moving back and forward using the Flash navigation buttons, you will see the browser Back and Forward buttons become active. Clicking on either of the browser buttons allows you to go back/forward through this history.

A lack of Back button functionality is one of the major usability problems normally pinned on Flash. Someone tell Jakob we've fixed it!

Of course, there are still a few issues here for advanced Flash users, like the fact that you need to create (and maintain) a blank HTML page for every Flash page you use, which can be a real pain and which breaks one of the really cool things about Flash design (i.e., it isn't page based). This brings up an irony of using the Back button in Flash. The question arises, where should the user go back to in the current Flash application? In an HTML-based site, each page represents the application state at a given time. But in a Flash application, each "scene" might span many frames and contain animations or time-based media. For example, if the user clicks the Back button to return to the previous scene, should the Flash designer rewind and replay the animation, audio, and video in the scene? Think carefully about your application's state when deciding which pages to use to represent a point in time.

A possible enhancement to this hack is to generate the HTML pages in the hidden frame dynamically via JavaScript. This way, the number of pages you can add to the browser history is not hardcoded, allowing for a more flexible solution for more complex Flash sites.

JavaScript-to-Flash communication is not supported in all browsers, as discussed at http://www.macromedia.com/support/flash/ts/documents/java_script_comm.htm. This hack works in Netscape and IE, but at least one user reported a JavaScript error when using Mozilla 1.6. You can test various browsers at Macromedia's JavaScript-to-Flash communication example page, at http://www.macromedia.com/support/flash/ts/documents/java_script_comm/javascript_to_flash.html.