27Nov/09Off

ASP.NET DefaultButton Property and FireFox

Whilst developing a website at work this week I encountered two issues with the use of default buttons in ASP.NET. Both issues were encountered when using FireFox, but potentially this may apply to all non-IE browsers. Note that this relates to ASP.NET 2.0, so things may be different or even hopefully fixed in later versions of the framework.

Issue 1: Using a LinkButton as the default button of a Panel control does not work in FireFox

The build of the website I have been developing uses styled hyper-links in place of submit buttons, so naturally I implemented these using LinkButton controls allowing for simple postbacks and server-side event handling. Wanting to avoid random submits when a user hits the return key in a form, I wrapped each form in a Panel control and set the DefaultButton properties to the relevant submit LinkButton controls. This works fine when viewing the site in Internet Explorer but does not work in FireFox.

Issue 2: Defining a default button for a Panel control breaks the return key in multi-line TextBox controls in FireFox

Several of the forms on the site require multi-line TextBox controls for such things as description fields. Once again this works fine in Internet Explorer, but has soon as the TextBox is within a Panel with a default button set it breaks in FireFox. Instead of breaking to a new line, the default button is clicked.

The cause

A bit of investigation revealed that both issues are caused by bugs in the JavaScript function that ASP.NET defines for implementing default buttons, named 'WebForm_FireDefaultButton'. Here is the standard function as defined in ASP.NET:

  1. function WebForm_FireDefaultButton(event, target) {
  2.    if (event.keyCode == 13 &&
  3.       !(event.srcElement && (event.srcElement.tagName.toLowerCase() == "textarea"))) {
  4.       var defaultButton;
  5.       if (__nonMSDOMBrowser) {
  6.          defaultButton = document.getElementById(target);
  7.       }
  8.       else {
  9.          defaultButton = document.all[target];
  10.       }
  11.       if (defaultButton && typeof(defaultButton.click) != "undefined") {
  12.          defaultButton.click();
  13.          event.cancelBubble = true;
  14.          if (event.stopPropagation) event.stopPropagation();
  15.             return false;
  16.          }
  17.       }
  18.       return true;
  19. }

The issue of LinkButton controls not working as default buttons in FireFox is related to lines 11 and 12. Line 11 checks that the control assigned as the default button has a function named 'click', which line 12 then calls if it does exist. This is fine for Internet Explorer, as the anchor tags do have this function, but in FireFox they do not. Therefore the Boolean expression in line 11 equates to false and nothing is 'clicked'.

The second issue of the return key breaking for multiline TextBoxes in FireFox when a default button is assigned is caused by line 3. There is a check for the tag of the source of the event not being 'textarea', so at first glance this looks okay, but a second looks shows that the expression uses 'event.srcElement'. This is an Internet Explorer specific property and so the Boolean expression on line 3 will always equate to true and allow execution into the code bound by the 'if' statement.

The fix for LinkButton controls as default buttons in FireFox

There are various examples on the Web showing how to fix the LinkButton issue. In short, the following code can be used on each page and for each Panel that uses a LinkButton control as a default button:

  1. <script type="text/javascript">
  2. <!--//--><![CDATA[//><!--
  3. $(function () {
  4.    var b = document.getElementById('<%= LinkButton.ClientID %>');
  5.    if (b && typeof(b.click) == 'undefined') {
  6.       b.click = function() {
  7.          var result = true;
  8.          if (b.onclick) result = b.onclick();
  9.          if (typeof(result) == 'undefined' || result) {
  10.             eval(b.getAttribute('href'));
  11.          }
  12.       }
  13.    }
  14. });
  15. //--><!]]>
  16. </script>

All this script does is wait until the page has loaded, gets a reference to the specified LinkButton control and defines a 'click' function for it. The 'click' function itself just executes any 'onclick' function and if this returns true the 'href' attribute is evaluated, which will execute the ASP.NET defined postback script.

This works very nicely but has the drawback that you have to copy & paste this for each and every Panel with a LinkButton control as a default button. See below for any easier way to implement this fix.

The fix for broken return keys in multi-line TextBox controls in FireFox when using default buttons

The concept behind the fix for issue 2 is really simple, redefine the bugged 'WebForm_FireDefaultButton' replacing the Internet Explorer specific code in the process. The problem is the use of 'event.srcElement'. In FireFox this needs to be 'event.target', so a cross-browser script can use:

  1. var source = event.srcElement || event.target;

The variable 'source' will hold a reference to whichever of 'srcElement' or 'target' that returns something. This reference can then be used in the following Boolean expression in the original function. In full, that gives the following function:

  1. function WebForm_FireDefaultButton(event, target) {
  2.     var source = event.srcElement || event.target;
  3.     if (event.keyCode == 13 &&
  4.        !(source && source.tagName.toLowerCase() == "textarea")) {
  5.         var defaultButton;
  6.         if (__nonMSDOMBrowser) {
  7.             defaultButton = document.getElementById(target);
  8.         }
  9.         else {
  10.             defaultButton = document.all[target];
  11.         }
  12.         if (defaultButton && typeof defaultButton.click != "undefined") {
  13.             defaultButton.click();
  14.             event.cancelBubble = true;
  15.             if (event.stopPropagation) {
  16.                 event.stopPropagation();
  17.             }
  18.             return false;
  19.         }
  20.     }
  21.     return true;
  22. }

Simply define this script in a js file and reference it with the following function call in the 'load' event of you master page if you have one, or individual pages if not:

  1. Page.ClientScript.RegisterClientScriptInclude("UpdatedDefaultButtonScript", _
  2.                                               ResolveUrl("~/defaultbuttonfix.js"))

Consolidation

Now that we have a redefined function we can integrate the LinkButton fix too. The resulting script can then be saved as a js file and referenced as above. If you are using a master page, this means that a single line of code fixes these two issues for the whole site. Please note that I have only just put this script together, so although it seems all good so far there may well be opportunities for improvement.

The final script, which you can download here, is as follows:

  1. function WebForm_FireDefaultButton(event, target) {
  2.     var source = event.srcElement || event.target;
  3.     if (event.keyCode == 13 &&
  4.        !(source && source.tagName.toLowerCase() == "textarea")) {
  5.         var defaultButton;
  6.         if (__nonMSDOMBrowser) {
  7.             defaultButton = document.getElementById(target);
  8.         }
  9.         else {
  10.             defaultButton = document.all[target];
  11.         }
  12.         if (defaultButton && typeof defaultButton.click != "undefined") {
  13.             defaultButton.click();
  14.             event.cancelBubble = true;
  15.             if (event.stopPropagation) {
  16.                 event.stopPropagation();
  17.             }
  18.             return false;
  19.         }
  20.       else if (defaultButton && defaultButton.tagName.toLowerCase() == "a")
  21.       {
  22.          var result = true;
  23.          if (defaultButton.onclick) result = defaultButton.onclick();
  24.          if (typeof(result) == 'undefined' || result) {
  25.             eval(defaultButton.getAttribute('href'));
  26.          }
  27.       }
  28.     }
  29.     return true;
  30. }
Comments (0) Trackbacks (0)

Sorry, the comment form is closed at this time.

Trackbacks are disabled.