Preventing Users from Accidently Navigating Away From Your Flex App

One of the downsides to creating Rich Internet Applicationss is that they defy the standard methods for interacting with websites — the refresh, back and forward buttons have no real place in an Flex application.  As a developer you probably understand that clicking refresh or back is a no-no but you users aren’t likely to be as savvy.  This article will describe a method you can employ to prompt users with a confirmation dialog when they attempt to navigate away from you Flex app. For this article I assume you are not using deep linking in your Flex application as I’m not currently using it with any of my projects so I cannot speak to using it in this scenario.  I’ve also only tested this with the Windows versions of Internet Explorer 7, Firefox 3.x, Chrome 1 and Safari 4 beta so if you’re using anything else your mileage may vary (will test with Mac browsers once I fix my Flash Player install).

With the disclaimer out of the way we’ll get down to business.  Our goal here is to produce a close confirmation popup when a user accidentally attempts to navigate away from our Flex app.  Examples of “navigating away from our Flex app” include: clicking the refresh, back or forward button, clicking a bookmark or favorite, or typing a new url in the address bar.  Additionally we’ll disable the prompt when the user intentionally navigates away from our Flex app; when logging out for example.  To accomplish this we’ll make use of the Javascript onbeforeunload event and Flex/Flash’s ExternalInterface package.

At this point I feel it’s important to note that doing what are about to implement is generally considered bad form for websites.  If I, as a user, want to leave a page I’d damn well better be able to with no hassle.  A Flex app is usually not a typical website however, rather it is an application running inside a web browser and when you close an application like Word with unsaved data you always get a confirmation dialog.

It’s my opinion that displaying a close confirmation for a Flex app is good form for two reasons.  The first is that RIAs are meant to be similar to desktop applications and any half decent desktop application is going to have you confirm closing the application if you have unsaved information.  The second is that, in being more like a desktop app, an RIA subverts the way users typically interact with web sites and web apps.  For most Flex apps the back and refresh buttons can cause havoc so throwing up a confirmation dialog when these buttons are clicked can enhance your application.

So we’ve made up our minds that we’re adding the close confirmation popup now lets get to it!

The Javascript

The first thing we need is some Javascript that will allow us to work with the onbeforeunload event.  Specifically we’ll need:

  • A flag to indicate whether or not the popup should be displayed
  • A variable to hold the text we want displayed in the popup
  • A function to set the flag
  • A function to set the prompt text

Here’s my Javascript object, FlexOnBeforeUnload that encapsulates all of this:


var FlexOnBeforeUnload = {

   /**
    * Flag indicating the user should be prompted each
    * time they attempt to navigate away from or refresh
    * the Admin HTML page.
    */
   prompt: true,

   promptText: "Refreshing or leaving this page will cause you "
             + "to lose any unsaved data.\n"
             + "Are you sure you want to leave?",

   /**
    * Sets the value of "prompt"
    */
   setPrompt: function (value) {
      if (value) {
         FlexOnBeforeUnload.prompt = true;
      } else if (!value) {
         FlexOnBeforeUnload.prompt = false;
      }
   }

   /**
    * Sets the value of "promptText"
    */
   setPromptText: function (value) {
      FlexOnBeforeUnload.promptText = value;
   }
};

Using Javascript we can access prompt and priompText directly however the ExternalInterface package in Flex only allows you to make calls to Javascript functions — you cannot use it to access variables directly. Besides accessor functions are good practice.

SetPrompt will take a boolean value and assign it to prompt. Setting prompt to true will cause the close confirmation to be displayed when a user attempts to navigate away from your Flex app. Setting prompt to false will allow users to navigate away without any nagging.

SetPromptText will allow you to change the text displayed in your confirmation dialog. Unfortunately you have very little control over the text displayed in the confirmation dialog. The dialog will always display “Are you sure you want to navigate away from this page?” and “Press OK to continue, or Cancel to stay on the current page.” along with the OK and Cancel buttons. The only thing you can customize is the text that appears between “Are you sure…” and “Press OK…”.

close-confirm.png

In the above pic you can see that “Refreshing or leaving this page will cause you to lose any unsaved data. Are you sure you want to leave?”, the default promptText, is displayed in the close dialog. Now that we have our Javascript object we’ll save it in a file called flex-onbeforeunload.js.

Now open up your HTML wrapper (the HTML page that loads your Flex app) and include flex-onbeforeunload.js in the head:


<!-- Assumes the JS file is located in the same folder as the wrapper. -->
<script src="flex-onbeforeunload.js" language="javascript">
</script>

After that you are ready to setup the onbeforeunload event handler.  In a script block beneath the include we just added add the following:


window.onbeforeunload = function(e) {
   if (FlexOnBeforeUnload.prompt) {
      return FlexOnBeforeUnload.promptText;
   }
}

This code will be executed prior to the page being unloaded (onbeforeunload even). If FlexOnBeforeUnload.prompt is true the close confirmation will be displayed with our promptText. If FlexOnBeforeUnload.prompt is false the function returns nothing and no confirmation will be shown.

The Actionscript

Since the default for FlexOnBeforeUnload.prompt is true the close confirmation dialog will always be shown unless we take some action to prevent it. Enter ExternalInterface, a class used to facilitate communication between Actionscript and Flash Player and the HTML wrapper. In other words, ExternalInterface allows Actionscript to talk with Javascript and vice versa, though in our situation we’ll only being going from Actionscript to Javascript.

For our purposes we are interested in two parts of ExternalInterface, the call method and the available property.  ExternalInterface.available “[i]ndicates whether this player is in a container that offers an external interface.”  In English, if ExternalInterface.available is true we can use ExternalInterface.  ExternalInterface.call allows us to call Javascript functions in the wrapper.  To use ExternalInterface.call you pass in the name of the Javascript function as a string and then any parameters the Javascript function takes as a comma-separated list.

Earlier I gave the example of logout as a situation where you would probably not want to show a close confirmation popup.  Using ExternalInterface you can disable the confirmation for situations such as this.  To disable the confirmation popup add the following to your logout code in Flex Builder:


// Make sure ExternalInterface is available
if (ExternalInterface.available)
{
   // Set prompt to false, disabling the close confirmation
   ExternalInterface.call("FlexOnBeforeUnload.setPrompt", false);
}
else
{
   // Optionally handle the situation where ExternalInterface
   // is not available.
}

This will set the value of FlexOnBeforeUnload.prompt to false, disabling the close confirmation. Similarly you can change the prompt text:


// Make sure ExternalInterface is available
if (ExternalInterface.available)
{
   // Set prompt to false, disabling the close confirmation
   ExternalInterface.call("FlexOnBeforeUnload.setPromptText",
                                "Different prompt text.");
}
else
{
   // Optionally handle the situation where ExternalInterface
   // is not available.
}

8 Comments

  1. vanessa says:

    i keep getting this error that says:

    Are you sure you want to navigate away from this page?

    Wait! Here’s where I write my message.

    Press OK to continue, or cancel to stay on the current page.

    what should i do to avoid it?

  2. Sean says:

    Vanessa,

    Sadly there is nothing you can do abou the “Are you sure you want to navigate away from this page?” and “Press OK to continue, or cancel to stay on the current page.” lines. All you can do is stuff your own message in between the two lines.

    In all my Googling I couldn’t find an explanation as to why onbeforeunload works this way, only that is does and there is no getting around it.

  3. Vanessa says:

    well i dont knw how but it went away! i made some changes on my laptop and then restarted it. it worked! the message is now gone.

    anyway, thanks for the reply. i really appreciate it.

  4. Matt says:

    What did you do Vanessa? Please tell us, this popup window is driving me crazy!!!

  5. Vaibhav says:

    Hi.. very good post. I have also implemented the same way in my application. But problem is, I want to logout the current user if the user selects OK in the dialogue.
    Can we trap the OK and do some required functionality in application and then return back the control to JS for reloading the page. ?

  6. Sean says:

    @Vaibhav: I don’t think there is a way to detect whether OK or Cancel was clicked save that a user who clicks cancel will still be on the page. And that doesn’t really help your situation. Have you tried listening for the onunload event? I believe this is fired after onbeforeunload.

  7. Bonnie says:

    Hi Sean,

    I’m confused as to where/when in my ActionScript code I should be making the ExternalInterface call. Meaning under what conditions?

    Or are you saying that it doesn’t matter where I place this call, it’s going to get called automatically when the browser dispatches the onbeforeunload event?

    Thanks,
    Bonnie

  8. Sean says:

    Hi Bonnie,

    It’s been awhile so I’ll do my best to make sure I get all of this straight. You must manually manually make your ExternalInterface call. There are two calls you can make:

    1) To set the prompt text (the message the user sees).
    2) To toggle the prompt value (whether or not the user should be prompted).

    Anytime you want to change one of these values you need to make the ExternalInterface call. For example, if you want to prompt a user if he navigates away from the app with some unsaved document you would make a call to set prompt to true when the document was unsaved and make another call to set prompt to false when the was saved.

Leave a Reply