0

IE appears not to always respond to a keyup event in one of my scripts.

I looked for an alternative way to detect if a key had been released.

Given that a held down key repeats the keydown event at intervals (except modifier keys on Mac), I thought it would be possible to increment a variable and listen for the point at which it stopped incrementing. When it stops incrementing, the key has been released?

Unfortunately, on occasions (not always), my script is detecting an end to the incrementing whilst the key is still held down. It tends to fail more if the key is held down for repeated short intervals. I have tested with IE and FF.

I have allowed for 2 seconds between checking each increment. Setting my Windows Control Panel to the slowest keyboard settings, 1 second would probably be sufficient.

<!DOCTYPE html>
<html>

<head>

  <title>Detect keyup not using keyup event using Javascript</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

  <script type="text/javascript">
    // opening variables
    var keyDownCount = 0;
    var nextLastTimeout1 = false;
    var nextLastTimeout2 = false;
    var lastCount = false;
    var nextCount = false;

     // function to compare the last two outcomes for keyDownCount by    assigning them to variables lastCount and nextCount
    function nextLastCount() {
      if (lastCount) {
        nextCount = keyDownCount;
        if (lastCount === nextCount) {
          // clear any outstanding timeouts
          clearTimeout(nextLastTimeout1);
          clearTimeout(nextLastTimeout2);
          // they match, display the count in the html
          document.getElementById('matched-next-last').innerHTML = keyDownCount;
        } else {
          // clear any outstanding timeouts
          clearTimeout(nextLastTimeout1);
          clearTimeout(nextLastTimeout2);
          // reset variable
          lastCount = false;
          // they don't match, call the function again after allowing sufficient time for the key repetition rate to increment the keyDownCount 
          nextLastTimeout1 = self.setTimeout("nextLastCount()", 2000);
        }
      } else {
        lastCount = keyDownCount;
        if (lastCount === nextCount) {
          // clear any outstanding timeouts
          clearTimeout(nextLastTimeout1);
          clearTimeout(nextLastTimeout2);
          // they match, display the count in the html
          document.getElementById('matched-next-last').innerHTML = keyDownCount;
        } else {
          // clear any outstanding timeouts
          clearTimeout(nextLastTimeout1);
          clearTimeout(nextLastTimeout2);
          // reset variable
          nextCount = false;
          // they don't match, call the function again after allowing sufficient time for the key repetition rate to increment the keyDownCount
          nextLastTimeout2 = self.setTimeout("nextLastCount", 2000);
        }
      }
    }

     // keydown listener
    document.addEventListener('keydown', function(e) {
      if (!e) e = window.event;
      // listen for alt key down
      if (e.altKey) {
        if (keyDownCount === 0) {
          // call nextLastCount() to start comparing the last two outcomes for keyDownCount
          // allow sufficient time for the key repetition rate to increment keyDownCount 
          setTimeout("nextLastCount()", 2000);
        }
        // increment the counter on each keydown repeat
        keyDownCount++;
        // display the current count in the html
        document.getElementById('display-count').innerHTML = keyDownCount;
      }
    });

     // keyup listener
    document.addEventListener('keyup', function(e) {
      if (!e) e = window.event;
      // listen for alt key released
      if (!e.altKey) {
        // clear any outstanding timeouts
        clearTimeout(nextLastTimeout1);
        clearTimeout(nextLastTimeout2);
        // reset the counter and the html fields when the keys are released
        keyDownCount = 0;
        document.getElementById('display-count').innerHTML = keyDownCount;
        document.getElementById('matched-next-last').innerHTML = "";
      }
    });
  </script>

</head>

<body>
  <p>Hold down the alt key to start the counter, relese to reset.</p>
  <p>keyDownCount is: <span id="display-count"></span>
  </p>
  <p>Matching next and last detected on key count of: <span style="color:blue;" id="matched-next-last"></span>
  </p>
</body>

</html>
Dylan Corriveau
  • 2,561
  • 4
  • 29
  • 36
martin
  • 393
  • 1
  • 6
  • 21
  • 1
    This possibly sounds like an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). If your window loses focus, it will no longer get key* events. EG press a key down, you get keydown, now click into another application and release the key - you don't see the keyup, as that event happened on the other application. Step back a bit, and possibly rethink what it is you're wanting to get this event for? – James Thorpe Apr 27 '15 at 12:40

2 Answers2

1

In the unlikely event that someone else might need this, I resolved it as follows. Simplified code and only one timeout.

In Firefox, pressing the spacebar whilst the alt key is down will serve to simulate a non keyup event.

<!DOCTYPE html>
<html>
<head>

<title>Detect keyup without using keyup event</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script type="text/javascript">

// opening variables
var keyDownCount = 0;
var nextLastTimeout = false;
var nextCount = false;
var lastCount = false;
var nextCountTime = false;
var lastCountTime = false;

// function to compare the last two outcomes for keyDownCount by assigning them to variables lastCount and nextCount
function nextLastCount() {
    if (lastCount) {
    nextCount = keyDownCount;
    // record the time for use in calculating the keyboard delay
    nextCountTime = +new Date(); // milliseconds since 01 January, 1970
        if (lastCount === nextCount) {
        // they match, display the count in the html
        document.getElementById('matched-next-last').innerHTML = keyDownCount;
        }else{
        // reset variable
        lastCount = false;
        }
    }else{
        lastCount = keyDownCount;
        // record the time for use in calculating the keyboard delay
        lastCountTime = +new Date(); // milliseconds since 01 January, 1970
        if (lastCount === nextCount) { 
        // they match, display the count in the html
        document.getElementById('matched-next-last').innerHTML = keyDownCount;
        }else{
        // reset variable
        nextCount = false;
        }
    }
}

// keydown listener
document.addEventListener('keydown',function(e) {
if(!e) e = window.event; 
    // listen for alt key down
    if (e.altKey) {
    // increment the counter on each keydown repeat
    keyDownCount++;
    // display the current count in the html
    document.getElementById('display-count').innerHTML = keyDownCount;
    // see below
    clearTimeout(nextLastTimeout);
    // call function
    nextLastCount();
        // calculate the keyboard delay i.e. time between repeated keystrokes
        if (nextCountTime && lastCountTime)  {
        // returns an always positive value in milliseconds 
        var keyboardDelay = Math.abs(nextCountTime - lastCountTime);
        }else{
        // in the first few increments both count times are not available, use an estimate
        var keyboardDelay = 3000; // also 500ms added below
        }
    // call nextLastCount() again, but on a delay that exceeds the keyboard delay
    // .. for safety, add 500ms to the calculated / estimated keyboard delay
    // this timeout will only complete when the increments stop
    // .. see clearTimeout(nextLastStickyTimeout) above
    nextLastTimeout = setTimeout("nextLastCount()",keyboardDelay + 500);
    }
 });

// keyup listener
document.addEventListener('keyup',function(e) {
if(!e) e = window.event; 
    // listen for alt key released
    if (!e.altKey) {
    // clear any outstanding timeouts
    clearTimeout(nextLastTimeout);
    // reset the counter and the html fields when the keys are released
    keyDownCount = 0;
    document.getElementById('display-count').innerHTML = keyDownCount;
    document.getElementById('matched-next-last').innerHTML = "";
    }
});

</script>

</head>

<body>
<p>Hold down the alt key to start the counter, release to reset.</p>
<p>In Firefox, pressing the spacebar whilst the alt key is down will simulate a non keyup event</p>
<p>keyDownCount is: <span id="display-count"></span></p>
<p>Matching next and last detected on key count of: <span style="color:blue;"id="matched-next-last"></span></p>
</body>

</html>
martin
  • 393
  • 1
  • 6
  • 21
0

This seems like something that should have already been solved. Here is a SO question on the best input libraries out there: which is the best Javascript Keyboard event library.(Hotkeys,Shortcuts )

Ideally you don't want to reinvent the wheel.

Community
  • 1
  • 1
Jim Buck
  • 2,383
  • 23
  • 42