67

I am trying to make a script run when Ctrl + Alt + e is pressed.
How can Tampermonkey fire on a simultaneous ctrl, alt, and e key?

I have tried ctrlKey, and altKey. I've found nothing that works.
How can I edit the script below to fire on Ctrl + Alt + e, instead of just e?

(function() {
  document.addEventListener("keypress", function(e) {
    if (e.which == 101) {
      var xhttp = new XMLHttpRequest;
      xhttp.onreadystatechange = function() {
        4 == xhttp.readyState && 200 == xhttp.status && eval(xhttp.responseText)
      }, xhttp.open("GET", "http://127.0.0.1:2337/inject", !0), xhttp.send();
    }
  });
})();
ThS
  • 4,597
  • 2
  • 15
  • 27
Rasspy
  • 673
  • 1
  • 5
  • 5

5 Answers5

109

Refer to the W3C spec for keyboard events. Several boolean attributes are provided to determine if modifier keys were pressed in conjunction with whatever target key you are interested in. They are:

  • ctrlKey     -- The "Control" key was also pressed.
  • shiftKey   -- The "Shift" key was also pressed.
  • altKey       -- The "Alt" key was also pressed.
  • metaKey     -- The "Meta" key was also pressed.

Other important notes:

  1. The which property is deprecated.
  2. Use keydown because Chrome does not fire the keypress event for known keyboard shortcuts.
  3. Some spec'd properties, such as key, are only partly functional in Firefox.
  4. You do not need to wrap your code in an anonymous function like that for Tampermonkey (or Greasemonkey or most userscript engines). Scope protection is automatically provided.

So, your code would become:

document.addEventListener ("keydown", function (zEvent) {
    if (zEvent.ctrlKey  &&  zEvent.altKey  &&  zEvent.key === "e") {  // case sensitive
        // DO YOUR STUFF HERE
    }
} );

Run this handy demo (updated now that key has full support):

var targArea = document.getElementById ("keyPrssInp");
targArea.addEventListener ('keydown',  reportKeyEvent);

function reportKeyEvent (zEvent) {
    var keyStr = ["Control", "Shift", "Alt", "Meta"].includes(zEvent.key) ? "" : zEvent.key + " ";
    var reportStr   =
        "The " +
        ( zEvent.ctrlKey  ? "Control " : "" ) +
        ( zEvent.shiftKey ? "Shift "   : "" ) +
        ( zEvent.altKey   ? "Alt "     : "" ) +
        ( zEvent.metaKey  ? "Meta "    : "" ) +
        keyStr + "key was pressed."
    ;
    $("#statusReport").text (reportStr);

    //--- Was a Ctrl-Alt-E combo pressed?
    if (zEvent.ctrlKey  &&  zEvent.altKey  &&  zEvent.key === "e") {  // case sensitive
        this.hitCnt = ( this.hitCnt || 0 ) + 1;
        $("#statusReport").after (
            '<p>Bingo! cnt: ' + this.hitCnt + '</p>'
        );
    }
    zEvent.stopPropagation ();
    zEvent.preventDefault ()
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<p><label>Press keys in here:<input type="text" value="" id="keyPrssInp"></label>
</p>
<p id="statusReport"></p>
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • Very insightful. Looks like a simpler solution for the case of using modifiers and just one target key code. For multiple keys, you'd have to keep track of what's selected with `keydown` and `keyup`. – Gideon Pyzer Jun 01 '16 at 12:02
  • @Gideon, true, but the use case for that is pretty rare in my experience. – Brock Adams Jun 01 '16 at 21:03
  • `zEvent.code` doesn't exist in Chrome, I was getting undefined until I realize this, you should use `zEvent.key` instead. – Carlos Mafla Mar 23 '18 at 04:47
  • @CarlosMafla, it still works just as it should. Reverified in Chrome 65.0.3325.181 and several other browsers. What browser version and OS are you using? – Brock Adams Mar 23 '18 at 05:45
  • @BrockAdams Version 65.0.3325.162 (Official Build) (64-bit) for Mac. – Carlos Mafla Mar 23 '18 at 18:47
  • @CarlosMafla, [Mac shows as still supported](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code#Code_values_on_Mac), and it's [still in the spec](https://www.w3.org/TR/uievents/#event-type-keydown). `key` wasn't used because it wasn't working right on FF at the time. Either should work now. – Brock Adams Mar 23 '18 at 22:02
  • has event.code been changed to either charCode or keyCode my intellisense on my IDE list either charCode or keyCode but not just code when I type event. ? Also listed is just event.char and event.key - but just event.code is not listed? Just wondering but if I type even.code I don't get an error running it so it definitely knows what it is? – Neal Davis Mar 05 '19 at 19:08
  • This example does not work in Edge for cntrl-alt-d or seemingly any 3 character presses like that as far as I can tell? With Edge going to chrome engine this year soon maybe not a concern to deal with? – Neal Davis Mar 05 '19 at 19:34
  • @NealDavis, updated the answer to reflect current browser states. Note that the Q is tagged [greasemonkey] so technically Edge does not apply. However the updated code *should* work in Edge. – Brock Adams Mar 05 '19 at 21:23
  • 2
    FYI, this fails in Firefox on OSX since pressing "alt" + "e" prints out this character: `´`. On event trigger, the `zEvent.key` attribute will be `´` instead of `e`. I got around this by using Ctrl + Shift + e, but keep cross platform in mind! – David Vitale May 16 '19 at 22:43
  • I know I'm four years late, but it didn't work for me as is (Chrome, Win10) I had to change addEventListener.() to on.() - now it works like charm – Zsolt Balla Dec 04 '20 at 15:08
  • Following @DavidVitale's comment, beware that this code would not work with European keyboard layouts where AltGr (which triggers also with Ctrl+Alt) + e gives out the € symbol. Moreover, this code fails if you press the shortcut in the "wrong order". ie: `Ctrl+Alt+G` outputs `The Control Alt g key was pressed.` while `Ctrl+G+Alt` outputs `The Control Alt key was pressed.` – Jamby Aug 18 '21 at 14:11
6

The keypress event is fired as soon as a key has been pressed. If you are pressing multiple keys, it will fire the event for each press, so they are considered independent key presses.

Instead, you can use both the keydown and keyup events to detect multiple key presses. You can have an object that contains the 3 keys and a boolean state. On the keydown event, if the current key matches a key in the object, you set the state to true for that key. On the keyup event, you reset the state for the current key to false. If all 3 states are true at the time of the last key press, then fire the event.

See this example that achieves this logic using jQuery.

Update Brock's answer is a better solution for you using modifier keys in combination with a single key code target, as the ctrlKey and altKey modifiers are detected in combination, not handled independently. If you want to detect multiple key codes like, E and F together, for example, you'd need to keep track of them using keydown and keyup as per above.

Gideon Pyzer
  • 22,610
  • 7
  • 62
  • 68
  • This is not a good approach for ***modifier*** keys like "Control", "Shift", "Alt", etc. – Brock Adams Jun 01 '16 at 05:09
  • I couldn't get Firefox to recognize the Alt Gr in combination with a letter key, only the combination with a plain Alt. So I used this method, though I had to modify it and fix it. It has some not so optimal logic. But the core idea of it works, of course. Be aware though that if your key event function is an async function containing an await, then the keyup event never fires! So extra logic is needed for that problem. But in the end, after all this was working nicely. I found the real solution, the event.getModifierState('AltGraph') which is what I was actually searching for from the start. – Magnus Sep 28 '20 at 07:22
1

If you, like me, came here to find out how to detect the combination of the "Alt Gr" key with a letter key, then the properties like for example event.altKey cannot be used for this, since there is no event.AltGrKey property.

In that case you can use

event.getModifierState('AltGraph')

For more details see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState

Magnus
  • 1,584
  • 19
  • 14
1

I'm using hotkeys-js to abstract the functionality, but still had to let the user choose his own custom shortcut (without knowing it in advance). I then tried @Brock Adams's answer to get an equivalent format of hotkeys-js of the shortcut pressed in an input box.

Things I've found while experimenting (on an Italian QWERTY keyboard layout on Chrome 92):

  • Every AltGr character "loses" the modifier attributes. AltGr can be substituted with Ctrl+Alt combination. So Ctrl+Alt+E (as in OP question) gives out . Same it's true for Ctr+Alt+5. Other combinations have the same problem, like Ctrl+Alt+ò which on Italian keyboards gives out @
  • Every Shifted character is found in e.key as it's shifted counterpart, keeping the modifier attributes, so Shift+5 prints out as Shift+% which is questionable (given the AltGr behaviour).
  • Some combinations don't trigger the event no matter what. I've found as examples Ctrl+Shift+9, Ctrl+Shift+0, Ctrl+Shift+L and Ctrl+Shift+Alt+D on my keyboard. That might be a limitation of membrane keyboards.

The second point was critical for me, since our clients are used to Ctrl+Shift+[Number] combinations.

I've solved it by handling the special case of keyCode being between 48 and 58 and converting manually to the corresponding digit. It still doesn't solve any other special character modified with Shift though, but given that they are extremely depended on the keyboard layout, I don't see any universal solution. Still, I've never known anyone that wanted a shortcut with special characters.

const input = document.getElementById("keyPrssInp");
input.addEventListener('keydown', e => {
  input.value = `${e.ctrlKey ? 'ctrl+' : ''}${e.shiftKey ? 'shift+' : ''}${e.altKey ? 'alt+' : ''}${e.metaKey ? 'meta+' : ''}${["Control", "Shift", "Alt", "Meta"].includes(e.key) ? "" : (e.keyCode < 58 && e.keyCode > 47 ? e.keyCode - 48 : e.key)}`
  e.preventDefault();
});
Press keys in here: <input type="text" value="" id="keyPrssInp">

If readability is not a requirement and/or you know in advance the key combination you want to check, using event.keyCode instead of event.key is probably safer.

Jamby
  • 1,886
  • 2
  • 20
  • 30
1

Quick note. If you're detecting shift, then you need to capitalize whatever letter you're checking. Shift capitalizes the normally lowercase letter.

if (zEvent.shiftKey && zEvent.key === "T")

vs

if (zEvent.ctrlKey && zEvent.key === "t")

if (zEvent.altKey && zEvent.key === "t")

RedDragonWebDesign
  • 1,797
  • 2
  • 18
  • 24