I have compiled the code (posted below):

var keys = [];
var key1="17"; //ctrl
var key2="16"; //shift
var key3="83"; //s

window.addGlobalHotkey = function(callback,keyValues){
    if(typeof keyValues === "number")
        keyValues = [keyValues];

    var fnc = function(cb,val){
        return function(e){
            keys[e.keyCode] = true;
            executeHotkeyTest(cb,val);
        };        
    }(callback,keyValues);
    window.addEventListener('keydown',fnc);
    return fnc;
};

window.executeHotkeyTest = function(callback,keyValues){
    var allKeysValid = true;

    for(var i = 0; i < keyValues.length; ++i)
        allKeysValid = allKeysValid && keys[keyValues[i]];

    if(allKeysValid)
        callback();
};

window.addEventListener('keyup',function(e){
    keys[e.keyCode] = false;
});

addGlobalHotkey(function(){
    var x = getIn("What Task?");
        switch(x)
        {
            case "a":
                //...
            break;
            case "t":
                //...
                break;
            default:
                //...;
        }
},[key1,key2,key3]);

function getIn(text){

    var x = prompt(text);

    if (x != null && x != ''){
        return x;
    }              
}
  • which allows for creation of a hotkey (in my case Ctrl+Shift+S)
  • designed to bring up an inputbox (aka prompt)
  • and take a command (some predefined switch or option)
  • defined in the section addGlobalHotkey(function(){/PLAY HERE/},[key1,key2,key3]);

So now that you have the whole background. It works great in Firefox, Chrome using both GreaseMonkey and TamperMonkey and does EXACTLY what I need it to do.

HOWEVER, I have noticed that after executing it on any given tab, that particular tab begins to gradually get slower, hanging, delayed paints and only solution is to close it. Other tabs are not affected, just the one you are on when executing. SO I am worried there might be a memory leak, is it something in the code that I am missing? Can it be improved somehow to ensure that's not the case?

Also, I have noticed that while 99+% of the time, it releases the keys just fine, there are occasions on which when you press ANY key on the keyboard and the command executes, suggesting that somehow the hook did not properly clean up after itself, causing any key to get trapped and process the trigger even when not the designated key, what did I miss for that to be happening? Can the stability be improved?

有帮助吗?

解决方案

There are a couple things you can do but I see no reason for memory leaks.

I have commented my edits.

var keys = [];

// Integers takes less space than strings, and we might as well put it in an array already
var shortcut = [
    17, // ctrl
    16, // shift
    83  // s
];

window.addGlobalHotkey = function(callback, keyValues){
    if(typeof keyValues === "number")
        keyValues = [keyValues];

    // Unfortunately, because you need the cb argument we can't define it outside the addGlobalHotkey method.
    var fnc = function(cb, val){
        return function(e){
            keys[e.keyCode] = true;
            executeHotkeyTest(cb, val);
        };        
    }(callback, keyValues);

    window.addEventListener('keydown', fnc);
    return fnc;
};

window.executeHotkeyTest = function(callback, keyValues) {
    var i = keyValues.length;

    while(i--) {
        // If key is not pressed, we might as well abort
        if(!keys[keyValues[i]]) return;
    }

    callback();
};

window.addEventListener('keyup',function(e) {
    // Remove key instead of setting it to false
    keys.splice(e.keyCode, 1);
});

addGlobalHotkey(function() {
    var x = getIn("What Task?");

    // If x is empty there's no point in resuming
    if(!x) return;

    // If-statements are faster than switches, just saying.
    switch(x)
    {
        case "a":
            //...
        break;
        case "t":
            //...
            break;
        default:
            //...;
    }
}, shortcut);

function getIn(text){
    var x = prompt(text);

    // Altered if statement
    if(typeof x === 'string' && x.length > 0) return x;
}

The most potential leak is the fact that the array keys will only grow as you press more different keys. Hence the .splice() method.


Edit

I have reviewed your code again. I couldn't help noticing that you create a new onkeydown listener whenever you register a new hotkey. I've changed that. Try the following code instead, and see if it still runs slow.

/* VARIABLES */

var keysPressed = [];
var hotkeys = [];

/* EVENT LISTENERS */

// Listen for keydown once instead for every hotkey
window.addEventListener('keydown', function(e) {
    // A button was pressed
    keysPressed[e.keyCode] = true;
    checkHotkeys();
}, false);

window.addEventListener('keyup', function(e) {
    // Remove key
    keysPressed.splice(e.keyCode, 1);
}, false);

/* FUNCTIONS */

function checkHotkeys() {
    var i = hotkeys.length;

    while(i--) {
        executeHotkeyTest(
            hotkeys[i].callback,
            hotkeys[i].keys
        );
    }
};

function addGlobalHotkey(callback, keys) {
    if(typeof keys === "number")
        keys = [keys];

    hotkeys.push({
        callback: callback,
        keys: keys
    });
};

function executeHotkeyTest(callback, keys) {
    var i = keys.length;

    while(i--) {
        // If key is not pressed, we might as well abort
        if(!keysPressed[keys[i]]) return;
    }

    callback();
};

function getIn(text){
    var x = prompt(text);

    // Altered if statement
    if(typeof x === 'string' && x.length > 0) return x;
}

/* OTHER */

// Integers takes less space than strings
var shortcut = [
    17, // ctrl
    16, // shift
    83  // s
];

addGlobalHotkey(function() {
    var x = getIn("What Task?");

    // If-statements are faster than switches, just saying.
    switch(x)
    {
        case "a":
            //...
        break;
        case "t":
            //...
            break;
        default:
            //...;
    }
}, shortcut);

Edit #2

I've found the issue!

So we've been using the keycode as index for the array - a numeric index. These are not associative. Have a look at the following example.

0: 'abc',
1: 'def',
2: 'ghi'

Delete 1

0: 'abc',
1: 'ghi'

Delete 2

0: 'abc',
1: 'ghi'

By converting them to a string, your array becomes an associate array and the right keys will be deleted.

// Listen for keydown once instead for every hotkey
window.addEventListener('keydown', function(e) {
    // A button was pressed
    keysPressed[e.keyCode.toString()] = true;
    checkHotkeys();
}, false);

window.addEventListener('keyup', function(e) {
    // Remove key
    delete keysPressed[e.keyCode.toString()];
}, false);

Check out the working demo: http://jsfiddle.net/rQePB/1/

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top