14

My app changes its state when a person holds modifier keys (Shift, Alt, Ctrl). I track modifier keys using keydown/keyup events:

var altPressed;
window.onkeydown = window.onkeyup = function(e) {
    altPressed = e.altKey;
}

Keyboard events don’t trigger outside of the browser tab. Now, imagine the following scenario:

  1. Hold Shift key
  2. Click on a link to my app, it will open in a new window
  3. Release Shift key

keyup event won’t fire on my page when it isn’t focused so my app will show when I focus on my app’s tab again it will show the Shift key still being pressed.

Would be nice if page visibility events had modifier key properties. Alas, they don’t.

document.addEventListener('webkitvisibilitychange', function(e) {
    if (document.webkitHidden) return;

    e.altKey // undefined :(

}, false);
NVI
  • 14,907
  • 16
  • 65
  • 104
  • So, you want to know what keys were pressed down *before* the page was loaded? If so, it might be a rough road... – rvighne Feb 28 '14 at 04:17
  • can you give a reason why you want to do that? the page could be loaded in background so it could not always get the key event – charlee Feb 28 '14 at 04:17
  • 3
    What problem are you actually trying to solve? So far, it looks like you're just describing a behavior you've observed and I'm not seeing a particular problem description that you need help with solving. – jfriend00 Feb 28 '14 at 04:29
  • @jfriend00: Check out the original revision; for some reason the OP edited out the actual question. – rvighne Feb 28 '14 at 04:34
  • I think the point is for alt/shift/etc to not still be pressed after returning to the app, in better terms, to detect it has been released when returning to the app. – JDuarteDJ Mar 11 '14 at 17:52

2 Answers2

4

The best I came up so far:

document.body.onkeydown = function(e) {
  if (e.which === 18) {
    alt_state.textContent = 'pressed';
  }
};

document.body.onkeyup = function(e) {
  if (e.which === 18) {
    alt_state.textContent = 'released';
  }
};

function detectAlt() {
  if (document.webkitHidden) return;
  window.addEventListener('mousemove', function onMove(e) {
    alt_state.textContent = e.altKey ? 'pressed' : 'released';
    window.removeEventListener('mousemove', onMove, false);
  }, false);
}

document.addEventListener('webkitvisibilitychange', detectAlt, false);
window.addEventListener('load', detectAlt, false);

Press alt key and click on the link: jsbin.

It relies on mousemove event which, unlike load and visibilitychange events, has altKey property. As a downside, it won’t detect altKey until a person moves the mouse.

NVI
  • 14,907
  • 16
  • 65
  • 104
  • mousemove keeps running detectAlt() all the time EVEN if it's just a if() return; it still is running all the time! (not down voted for this, its still a solution) – JDuarteDJ Mar 11 '14 at 17:18
  • 1
    @JDuarteDJ onMove runs on each mousemove, but if you check out the code, after it runs the first time it is removed. detectAlt runs when the load and webkitvisibilitychange events are triggered. – Tibos Mar 11 '14 at 17:34
  • still most of the time it is ON. Since Load until alt down and then since visible until next alt down. Basically whenever its not idle! What do you think of my answer? – JDuarteDJ Mar 11 '14 at 17:40
  • I found another issue: sometimes it doesn't detect alt key pressed, most probably because it was pressed "between" mousemove triggers or something... – JDuarteDJ Mar 11 '14 at 17:55
  • 1
    I tried to make a solution based on setTimeout and custom event dispatch. Unfortunately, I werent able to get shift/alt/ctrl key state without firing native event (keydown keypress keyup mouseenter mouseleave mousemove mouseout mouseover). And I can't force user to trigger one of these. Your solution is the best so far. You can only extend it with all events I mentioned, so state will be updated with almoast any user interaction, not just mousemove. Good luck – Entity Black Mar 11 '14 at 19:18
  • The trick here is to to save the modifier status to your own vars. – JDuarteDJ Mar 12 '14 at 00:00
1

I could think of 2 options:

  1. Use a timed "alt status" --> after 2 seconds declare alt unpressed.

    document.body.onkeydown = function(e) { if (e.which === 18) { alt_state.textContent = 'pressed'; setTimeout(function(){ alt_state.textContent= ""; },2000); } };

  2. When visibility is lost simply reset all alt flags.

    document.addEventListener('webkitvisibilitychange', function(e) {
        if (document.webkitHidden){
            altPressed = "";
            return;
        }
    }, false);
    

try this: http://jsbin.com/jihuyibu/1/edit It's my best guess, even so it never comes out perfectly.

JDuarteDJ
  • 1,073
  • 12
  • 25
  • Reset... all the things? – xyhhx Mar 11 '14 at 19:12
  • I don’t see this working. Also, You’re linking to my own jsbin. – NVI Mar 11 '14 at 23:38
  • @Martin yes reset all your modifier flags. NVI i made a small change in your code using 2. Take a closer look – JDuarteDJ Mar 11 '14 at 23:55
  • Tomorrow ill improve the answer but basically the only problematic station is pressing the modifier outside the app and detecting it pressed when entered. For that part alone id go with a version of your solution: onvisibility change add a listner to mousemove then remove it at first trigger recording the state of your modifier key. That would also solve the opposite case detecting release outside the app. Still second option is to use assumptions: assume when leaving keys are released, assume when entering no keys are pressed. That could make it easier. – JDuarteDJ Mar 12 '14 at 00:21