Jan 4, 2009

Standardize window.onbeforeunload behavior with jQuery

Background

All modern browsers support the window.onbeforeunload event. This event allows web application developers to warn users that they might abandon unsaved changes before they navigate away from a page. Under specific conditions, Internet Explorer behaves differently than other browsers like Firefox and Chrome.

The quirky behavior is observed for HREF JavaScript links (example: <a href="javascript:__doPostBack();">). Many ASP.NET Web Form Controls are fond of rendering such links.

Problem

Here's some sample code which triggers the quirky behavior. Notice that the link doesn't cause the browser to redirect to any other page; it simply calls window.alert.

<a onclick="alert('onclick');" href="JavaScript:alert('href');">I'm a link, click me</a>
<script type="text/javascript">
    window.onbeforeunload = function() { return 'onbeforeunload' };
</script>

When a user clicks on the link, the expected behavior is:

  1. alert: onclick
  2. alert: href

But it appears as if Internet Explorer doesn't care that the page isn't actually unloading and will always show the onbeforeunload message:

  1. alert: onclick
  2. onbeforeunload message is displayed
  3. alert: href

Solution

I’ve written a jQuery plug-in that modifies these types of links to achieve consistent behavior across browsers.  This is accomplished by modifying the links so that href script is actually called during the onclick event. It then sets the href attribute to “#” so that Internet Explorer won’t unnecessarily raise the onbeforeunload event.

Code

(function($) {
   $(document).ready(function() {

       $('a').filter(function() {
           return (/^javascript\:/i).test($(this).attr('href'));
       }).each(function() {
           var hrefscript = $(this).attr('href');
           hrefscript = hrefscript.substr(11);
           $(this).data('hrefscript', hrefscript);
       }).click(function() {
           var hrefscript = $(this).data('hrefscript');
           eval(hrefscript);
           return false;
       }).attr('href', '#');

   });
})(jQuery);

Usage

Assuming you've placed the code into a file named jquery.fixie-onbeforeunload.js then you can conditionally include it for Internet Explorer like this:

<html>
    <head>...</head>
    <body>
        ...
        <!--[if IE]>
            <script type="text/javascript" src="jquery.fixie-onbeforeunload.js"></script>
        <![endif]-->
    </body>
</html>

8 comments:

  1. great piece of code, works like magic.
    thank you.

    ReplyDelete
  2. Ken - nice tweak. Works great on my end. One small fix. The line:

    }).click(function() {

    should be changed to the live event handler version:

    }).live("click", function() {

    This makes sure that any other hooked up click handlers (through .bind for instance) aren't blown away / detached. Using .live should also bind to any new dynamically added anchors.

    ReplyDelete
  3. Thanks for this, i had a scenario where i could not use onClick events for my javascript actions. It was casing problems with IE, not now!

    ReplyDelete
  4. Many thanks for this information! All the info Google turns up about the onbeforeunload event seems to relate to people wanting to pop up 'are you sure' messages, but nowhere could I find any information about FireFox not firing the onbeforeunload event on a Javascript href.

    I like your solution, but for me I wanted to either achieve the opposite of what you have done here, or to change the way the functionality is implemented.

    My solution was to change the href to be "#" and add the javascript call to the onclick event, and manually call the same function called in onbeforeunload when I want it to be triggered and it works in FireFox and IE.

    ~Cyrix

    ReplyDelete
  5. Thank you very much! I normally dont like IE, but the javascript:void(0) thing was driving me nuts! We had done that rather than # and saw the identical behavior. Crazy!

    ReplyDelete
  6. thanks a lot, helped me with the tinymce toolbar buttons!!

    ReplyDelete
  7. Thanks a bunch for this code! It works great

    ReplyDelete