3

How can my javascript program determine which modifiers were pressed along with a keydown event in Chrome on Linux?

I tried this code:

document.addEventListener('keydown', function(e)
{
    console.log("KEY DOWN: key=" + e.key + ", code=" + e.code
           + ", meta=" + e.metaKey + ", alt=" + e.altKey);

    // stop propagation, except don't kill F12.
    if (e.code != 'F12')
    {
        e.preventDefault();  // Windows and Firefox
        e.stopPropagation();
        return false;
    } // else: if propagating: Windows wants undefined result.
});

And, while it works in Firefox:

// output when I type M-q A-q in Firefox Quantum 67.0.4 (64-bit)
KEY DOWN: key=Meta, code=OSLeft, meta=true, alt=false
KEY DOWN: key=q, code=KeyQ, meta=true, alt=false
KEY DOWN: key=Alt, code=AltLeft, meta=false, alt=true
KEY DOWN: key=q, code=KeyQ, meta=false, alt=true

Linux Chrome seems to be modifier-challenged:

// output when I type M-q A-q in Linux Chrome Version 66.0.3359.181 (Official Build) (64-bit)
KEY DOWN: key=Meta, code=MetaLeft, meta=false, alt=true
KEY DOWN: key=q, code=KeyQ, meta=false, alt=true
KEY DOWN: key=Alt, code=AltLeft, meta=false, alt=true
KEY DOWN: key=q, code=KeyQ, meta=false, alt=false

Evidently, the metaKey and altKey fields of the KeyboardEvent are not cross-browser. Or I didn't turn on Chrome's "modifier keys" feature (how do I do that?). Or something.

The above code works just fine in both Edge and Chrome in Windows. (Windows will catch the Windows key if it's the only modifier, but your application can perfectly well catch combinations like Ctrl-Meta-x). For example, the code above has the same output in both Edge and Chrome in Windows when I type C-S-M-Q A-Q:

KEY DOWN: key=Control, code=ControlLeft, meta=false, alt=false
KEY DOWN: key=Shift, code=ShiftLeft, meta=false, alt=false
KEY DOWN: key=Meta, code=MetaLeft, meta=true, alt=false
KEY DOWN: key=Q, code=KeyQ, meta=true, alt=false
KEY DOWN: key=Alt, code=AltLeft, meta=false, alt=true
KEY DOWN: key=q, code=KeyQ, meta=false, alt=true

(note one small difference between Chrome and Edge: Edge has code=undefined. However there is still enough information in the KeyboardEvent to determine which key was pressed, except possibly Windows has some issue with combining NumLock + Shift + Keypad number).

The last time I used a Mac, it supported both the Meta and Alt keys, so the question probably applies to Mac too.

In Linux, be sure your computer has working Meta and Alt keys, as some popular distros are broken out of the box. If it's set up correctly, xmodmap should say:

shift       Shift_L (0x32),  Shift_R (0x3e)
lock        Caps_Lock (0x42)
control     Control_L (0x25),  Control_R (0x69)
mod1        Meta_L (0x85),  Meta_R (0x86)
mod2        Num_Lock (0x4d)
mod3        Alt_L (0x40),  Alt_R (0x6c)
mod4      
mod5        ISO_Level3_Shift (0x5c),  Mode_switch (0xcb)

because programs interpret mod1 as Meta and mod3 as Alt. (Use the xev utility to get the key codes for your keyboard.) With settings as shown above, Firefox, Emacs, and other programs can catch all the modifier keys correctly.

What is the correct method to determine the modifiers that were pressed along with a keydown event in Linux Chrome?

personal_cloud
  • 2,846
  • 1
  • 19
  • 25

2 Answers2

2

I came up with something that works consistently across Chrome/Firefox/Edge:

let modifier_challenged = /Google/.test(navigator.vendor);
let last_mod            = {Meta: false, Alt: false};

function keydown(e)
{
if (modifier_challenged)
{
    last_mod[e.key] = true;
    e = {key:      e.key,
         code:     e.code,
         shiftKey: e.shiftKey,
         ctrlKey:  e.ctrlKey};
    e.metaKey = last_mod['Meta'];
    e.altKey  = last_mod['Alt'];
}
console.log("KEY DOWN: key=" + e.key + ", code=" + e.code
        + ", meta=" + e.metaKey + ", alt=" + e.altKey);
}
function keyup(e)
{
if (modifier_challenged)
    last_mod[e.key] = false;
}

document.addEventListener('keydown', keydown);
document.addEventListener('keyup',   keyup);

But I would prefer an answer that also works if the modifier was most recently pressed or released outside the window (before keyboard focus was moved back into the window). Maybe not possible?

Update

Well, the above code is fine if you only use one modifier at a time. But if you want to combine many modifiers, Chrome still has a problem... It changes the key value from Alt to Meta if you are also holding the Shift key. So we have to use:

let modif_codes = /^Control|^Shift|^Meta|^Alt|^OS/;

function set_last_mod(e, value)
{
if (!modif_codes.test(e.key))
    return;
let key_sym = e.code.replace(/([A-Z])/g, " $1").split(" ")[1];
if (key_sym != e.key)
{
    console.log("Warning: code(" + e.code
        + ") does not match key(" + e.key
        + "). Using " + key_sym + ".");
}
last_mod[key_sym] = value;
}

in place of last_mod[e.key] = value.

personal_cloud
  • 2,846
  • 1
  • 19
  • 25
1

I think the best method for keyboard event is KeyboardEvent.getModifierState() The KeyboardEvent.getModifierState() method returns the current state of the specified modifier key: true if the modifier is active.

  • At least as of Firefox 48, the ⊞ Windows key is not considered the "Meta" key. KeyboardEvent.metaKey is false when the ⊞ Windows is pressed.

ref: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState

Also, I always check user-agent then I send a notice for the user that which key usable.

Hossein Shourabi
  • 318
  • 2
  • 13
  • Windows key is not the "Meta" key? Are you using a keyboard that has some other key between "Ctrl" and "Alt"? Or then where your "Meta" key (in other programs, that are working)? – personal_cloud Jul 07 '19 at 19:44
  • Thanks for that link. I tried `getModifierState()` using `keyArg` values of `Alt` and `Meta`. The result is much the same as the `altKey` and `metaKey` fields... it works in Firefox, but not in Chrome. Chrome thinks my Meta key is Alt... and it thinks my Alt key itself has the Alt modifier, but fails to notice when combined with another key. – personal_cloud Jul 07 '19 at 19:46