7454 Views // 0 Comments // Not Rated

Reasonable Handler For Close (X) Click Event On The Browser

Here's a little trick I came up with to "handle" the "event" when the user clicks the close (X) button on Internet Explorer. I use quotes because there really isn't an event for this action, and what I do with it isn't really handling an event either. But it works!

The situation is an ASP.NET 2.0 web application which has modal dialog windows that process information passed from (or, more accurately, shared between) parent window and child window via session state. The modal windows, each having OK and Cancel buttons, clone this data and manipulate it. My Cancel button, as well as the X button on the browser, need to call a JavaScript method on the parent, then do a self.close() to avoid any error messages when closing the window. The OK button does the same, but needs an event handler to call code on the child window to move the cloned data back into session so the parent window can be updated.

Note: I mentioned, explicitly, Internet Explorer only in the introduction. As far as cross-browser functionality goes, I've tried this on both browsers: IE 6 and 7. Haha. Since this was for an internal project, we didn't have to support any other browsers. Go ahead and try it though; it shouldn't too far off from playing nice with FireFox, etc.

Another Note: Although we are working with modal dialogs, I do not use window.openModalDialog. I am popping these windows up through a custom AJAX script control, and openModalDialog blows up with an "Object does not support this property or method." JavaScript exception. Also, there's a lot of complicated AJAX-y and databind-y stuff going on in the popups, and I found in development that using window.openModalDialog actually made a lot of this not work! So I'm just not going there; instead, the AJAX control I use puts a few nice translucent divs over the main page to give it a cool "modal-ish" effect. But again, try it out!

Okay, no more notes. Let's look at some code. There is a magical event named "onbeforeunload" that we can hook on the body tag of our web form. It fires, roughly, just before the window closes...tantamount to an "IsClosing" event. The bad news is that we just can't put a self.close() call in there, since we have no opportunity to react to OK vs. Cancel clicks or possibly get into managed code. The good news is that the OnClientClick events of our buttons fire before this event, so we can work around it!

  1. In script on the popup page, create a variable to act as a flag to indicate if we want the code in onbeforeunload to fire.  
  2. Hook onbeforeunload in the body tag.  The script should contain merely an if statement checking if the flag from Step #1 is true.  In such a case, do whatever JavaScript you need to (in my case, it was a call to the parent window using "Opener") and then do a self.close().  
  3. Hook OnClientClick of your OK and Cancel (or whatever) buttons.  The code in these events first turns off the flag in Step #1, which basically disables onbeforeunload.  It then does whatever processing you need, and finally calls self.close().  The reason for all of this is because the OnClick event in your managed code will still be allowed to fire, the JavaScript in OnClientClick will still be allowed to run, and the self.close() will still be allowed to close the window without bugging the user with a security message.  We need to first disable onbeforeunload; otherwise, its self.close() will close the window before everything that we need to happen in the button's click events happens.

This way, you can pretty much do the same thing in your onbeforeunload event as you do in your Cancel button's OnClientClick event. The one situation I don't cover is if you need to call code when the browser window is closing. You can try a __doPostBack in the onbeforeunload event, but I'm not going here since a cancel should really just close the window immediately. If you find yourself needing code in this situation, reconsider your approach. What I had to do was clone everything so that a cancel merely disposed and closed; an OK posted back, popped up a "Please wait" div with a pretty spinning div, and rebound the UI. After an OK, it's acceptable to post back: the users will intuitively expect the app to churn after an affirming action.

Here's some example markup:

Code Listing 1

  1. <html>
  2. <head>
  3. var shouldClose = true;
  4. function Cancel()
  5. {
  6. opener.CancelClicked();
  7. self.close();
  8. }
  9. </script>
  10. protected void lnkOK_Click(object sender, EventArgs e)
  11. {
  12. //do stuff
  13. ...
  14. //close window
  15. ScriptManager.RegisterStartupScript(this, typeof(Page), Guid.NewGuid().ToString(), "self.close();", true);
  16. }
  17. </script>
  18. </head>
  19. <body onbeforeunload="if (shouldClose) { Cancel(); }">
  20. <table>
  21. <tr>
  22. <td>
  23. <asp:UpdatePanel ID="upJustForFun" runat="server">
  24. <ContentTemplate>
  25. <asp:LinkButton ID="lnkOK" runat="server" Text="OK" OnClientClick="shouldClose = false; opener.OKClicked();" OnClick="lnkOK_Click" />
  26. </ContentTemplate>
  27. </asp:UpdatePanel>
  28. </td>
  29. <td>
  30. <asp:LinkButton ID="lnkCancel" runat="server" Text="Cancel" OnClientClick="Cancel();" />
  31. </td>
  32. </tr>
  33. <tr>
  34. <td>
  35. Here is a tree:
  36. </td>
  37. <td>
  38. <div onmouseover="shouldClose = false;" onmouseleave="shouldClose = true;">
  39. <asp:TreeView ID="tvIKnowThisIsAHackButItTotallyWorks" runat="server" />
  40. </div>
  41. </td>
  42. </tr>
  43. </table>
  44. </body>
  45. </html>

One thing to call your attention to: that TreeView. The JavaScript calls that ASP.NET generates for the expanding and collapsing of the tree will actually cause onbeforeunload to fire. For this and any other (I haven't seen any others...including AutoPostBacks) just wrap it in a div that blocks onbeforeunload when the user enters or leaves (IE only...sorry...try onmouseout for other browsers) the offending control.

No Tags

No Files

No Thoughts

Your Thoughts?

You need to login with Twitter to share a Thought on this post.