views:

335

answers:

2

I have a web front-end to an AS/400 CGI application which allows the use of some of the F1-F24 keys (depending on the page) as well as page-up, page-down etc - these are passed to the underlying application which handles them appropriately. For instance, on a given page, a user could either press the F3 button or press the F3 key - both of them will set the (hidden) CmdKey variable to have a name of '_K03' and a value of 'F03'. The button handling is simple and has no problems. To handle users pressing an actual F-key on the keyboard, I have had an IE-compatible script for a long time which works perfectly:

function setCmdKeyIE() {                                                        
  var cmdkeycode = "";                                                          
  if (window.event.keyCode != 13 &
    window.event.keyCode != 33 &                 
    window.event.keyCode != 34 &
    window.event.keyCode < 112 ) return;         
  window.event.keyCode = window.event.keyCode + 1000;                           
  if (window.event.shiftKey) window.event.keyCode = window.event.keyCode + 1000;
  switch(window.event.keyCode) {                                                
    case 1013: cmdkeycode = "EN"; break; /* Enter */                            
    case 1033: cmdkeycode = "UP"; break; /* Page Up */                          
    case 1034: cmdkeycode = "DN"; break; /* Page Down  */                       
    case 1112: cmdkeycode = "01"; break; /* F1 */                               
    case 1113: cmdkeycode = "02"; break; /* F2 */                               
    ...(F3 thru F24 here)...
    default:   return;                   /* Anything else should be ignored */
  }                                                         
  window.event.cancelBubble = true;                         
  window.event.returnValue = false;                         
  document.forms[0].CmdKey.value = "F" + cmdkeycode;        
  document.forms[0].CmdKey.name = "_K" + cmdkeycode;        
  if (ONSUBMITFUN() == true) document.forms[0].submit();    
}                                                           

This not only sets the CmdKey element correctly, but it also overrides (stops) the browser default behavior (if any) from being executed (For instance, when the user presses F3, the Search box doesn't appear).

The setCmdKeyIE() function is invoked thus:

<body onKeyDown="setCmdKeyIE();" onHelp="return false;">

I now need this to work for Firefox (and, potentially other browsers) and I'm having all sorts of trouble. I initially changed the setCmdKeyIE function (yes, I know the name should be changed once it's no longer IE-specific, but that's the least of my worries!) to get the event as a parameter (which would only be the case with FF, I thought) or to use the current behavior if it's not passed (with IE). I also added some other processing to stop FF event propagation, but it isn't working...

Here's the new non-working code - can some kind soul point out the error of my ways?

function setCmdKey(e) {
  if (!e) {
    var e = window.event; /* IE event-handling */
  }
  var wrkkeyCode = e.keyCode;
  if (wrkkeyCode != 13 &
      wrkkeyCode != 33 &
      wrkkeyCode != 34 &
      wrkkeyCode != 27 &
      wrkkeyCode < 112 ) return;
  wrkkeyCode = wrkkeyCode + 1000;
  if (e.shiftKey) wrkkeyCode = wrkkeyCode + 1000;
  var cmdkeycode = "";
  switch(wrkkeyCode) {
    case 1013: cmdkeycode = "EN"; break; /* Enter */
    case 1033: cmdkeycode = "UP"; break; /* Page Up */
    case 1034: cmdkeycode = "DN"; break; /* Page Down  */
    case 1112: cmdkeycode = "01"; break; /* F1 */
    case 1113: cmdkeycode = "02"; break; /* F2 */
    ...(F3 thru F24 here)...
    default:   return;               /* Anything else should be ignored */
  }
  if (e.stopPropagation) {           /* FF */
    e.stopPropagation();
    e.preventDefault();
  }
  else {                             /* IE */
    e.cancelBubble = true;
    e.returnValue = false;
  }
  document.forms[0].CmdKey.value = "F" + cmdkeycode;
  document.forms[0].CmdKey.name = "_K" + cmdkeycode;
  if (ONSUBMITFUN() == true) document.forms[0].submit();
}

Do I need to return false from setCmdKeyIE with FF? Does this hold true even if this procedure returns false?

+2  A: 

UPDATED

I've sorted this out now. Sorry for removing the context for the comments below, but hey, the previous versions are still there.

It turns out that there are two problems to be fixed in IE: first is that contrary to what I said before, putting an onkeydown attribute in the <body> doesn't work. You need to attach it to the document instead. The second issue is that IE won't let you suppress magic behaviour such as the search dialog triggered by the F3 key unless you do an evil hack involving changing the keydown event's keyCode property, which is clearly a Very Wrong Thing Indeed. Be that as it may, remove the onkeydown attribute in the <body> and the following should do the job (now amended to work in Opera also):

var keyCodeMap = {
    "1013": "EN",
    "1033": "UP",
    "1034": "DN",
    "1112": "01",
    "1113": "02",
    "1114": "03"
    // ...(F4 thru F24 here)...
};

var suppressKeypress = false;

function setCmdKey(e) {
    e = e || window.event;
    var wrkkeyCode = e.keyCode;
    if (wrkkeyCode != 13 &&
        wrkkeyCode != 33 &&
        wrkkeyCode != 34 &&
        wrkkeyCode != 27 &&
        wrkkeyCode < 112) return;

    wrkkeyCode += 1000;

    if (e.shiftKey) wrkkeyCode += 1000;
    var cmdkeycode = keyCodeMap[wrkkeyCode];
    if (!cmdkeycode) return; /* Anything else should be ignored */

    var input = document.forms[0].elements["CmdKey"];
    input.value = "F" + cmdkeycode;
    input.name = "_K" + cmdkeycode;

    try {
        // Prevent default action in IE by bad hacky means
        e.keyCode = 0;
    } catch (ex) {
        // Other browsers do not allow setting the keyCode
    }
    suppressKeypress = true;

    if (ONSUBMITFUN()) document.forms[0].submit();
    return false;
}

document.onkeydown = setCmdKey;
document.onkeypress = function() {
    if (suppressKeypress) {
        return false;
    }
};
Tim Down
@Tim, I still need the IE event handling to be stopped via cancelBubble, otherwise the IE default event still occurs (in addition to the page being submitted).
roryhewitt
Which key? I'm curious, as I wouldn't expect that to be the case.
Tim Down
In IE, if I use your script and press e.g. F3, the page isn't submitted - the search box is displayed instead. I haven't checked your script in FF - I was checking IE backwards-compatibility first.My assumption is that because this script submits the page, it never gets to the final 'return false' statement - could that be it?
roryhewitt
That could be it. I'll do a little research.
Tim Down
Answer rewritten.
Tim Down
In Opera, you can't suppress the default action `onkeydown`, you have to use `onkeypress` to do so. Also see Jan Wolter's [JavaScript Madness: Keyboard Events](http://unixpapa.com/js/key.html).
Marcel Korpel
Ha! That could have been one of my comments: I've pointed that fact out many times on SO, with links to that same page. Yes, of course you're right. I'll amend.
Tim Down
Done. Now works in Opera.
Tim Down
Nice solution. I usually used a different keyboard handler for Opera (which distinguishes between `event.which` and `event.keyCode`) attached to a `keypress` event if `window.opera` was present.
Marcel Korpel
Yes, that would work. I tend to go to extreme lengths to avoid using browser sniffs wherever possible, even for relatively innocent ones like checking for the existence of `window.opera`.
Tim Down
I understand, I usually do that, too. BTW, don't you have to run `event.preventDefault()` in Opera to suppress the default action, or is returning `false` enough?
Marcel Korpel
I'm pretty sure Opera is the same as other browsers and `return false` is enough when the event listener is assigned via a property rather than via `addEventListener`. It had the desired effect in this case in Opera 10.6. I will go away and check though.
Tim Down
Oh no! I didn't know it differed whether your event listener was assigned via a property or using `addEventListener`. But, taking the [W3C specification](http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-Event-preventDefault) into account, I understand the latter is more adhering to the standards.
Marcel Korpel
There is mention of so-called "DOM0" event handlers in the HTML 4.01 spec: http://www.w3.org/TR/REC-html40/interact/scripts.html. Besides, they've been in browsers for so long and are so widely used that I don't think they'll be going away any time soon.
Tim Down
A: 

You should always return false from event handlers even if you have prevented default by modifying the event, see Quirks mode for more information.

Kristoffer S Hansen