Accept simultaneous keystrokes in text input (adaptation of textInput demo code)

I’ve been using the implementation given here for collecting text input responses. In my experiment, participants have to type the numbers they just saw presented on the screen, e.g. ‘3482’. The typed letters appear on screen and can be edited using backspace.

This is a great solution, but I noticed one small issue with it: if you press multiple keys at once, only one key is actually registered and gets added to the typed text (and the recorded response). Therefore, there is a risk of keypresses being missed if a participant types quickly. In principle, they can see what they typed and correct themselves, but that requires a certain level of vigilance and a mistake is easily made.

So, I adapted this code to handle multiple simultaneous keypresses. My full code is pasted below. This is the JS code but the Python code is very similar (just embed the code from the textInput demo in a for loop).

The first for-loop is actually doing something unrelated but perhaps also useful to others: it ensures that if you use the numpad keys to type numbers, they show up just as numbers, rather than ‘num_0’, or ‘num_1’ etc. (the keys in the number row are just named ‘0’, ‘1’, ‘2’ and so on, whereas the numpad keys are preceded by ‘num_’).

The next for loop then goes through all the keys that were registered since the last check. I’m not sure in what order they are actually returned by ‘getKeys’, so if that matters to you, you should dig into that. Also I’m not taking special care about whether special keys get processed first as I’m assuming my participants will never type something and press ‘return’, ‘space’, ‘backspace’ or ‘escape’ at the same time. I’m primarily interested in properly handling the case where they type two characters at once, which will be common for fast typers (certainly happens to me when I pilot on myself).

Also note that I’m not restricting the characters that can be typed (e.g. to only numbers), so this code is pretty universal in that respect. What is peculiar to my setting is that I’m using ‘return’ and ‘spacebar’ as confirmation keys - when the user presses one of those, it ends the response window and “locks in” whatever text the user typed up to that point. I also impose a minimum response time (‘rsp_min_dur’), so they can’t confirm their response before that minimum time has elapsed (to prevent people from rushing through the experiment).

Finally, you should know that ‘text’ is the name of the textbox whose content is being modified based on the user’s keystrokes.

Anyway, just posting this in case it is useful to anyone.


This code gets executed every frame:

let theseKeys = psychoJS.eventManager.getKeys();
for (var i, _pj_c = 0, _pj_a = util.range(theseKeys.length), _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
    i = _pj_a[_pj_c];
    theseKeys[i] = theseKeys[i].replace("num_", "");
}

if (theseKeys.length > 0) {  // at least one key was pressed
    for (var i, _pj_c = 0, _pj_a = util.range(theseKeys.length), _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c +=1) {
        i = _pj_a[_pj_c];
        let textAdd = theseKeys[i];
        if (['return', 'space'].includes(textAdd)) {    
            textAdd = '';  // Add nothing
            if (t>rsp_min_dur) {
                continueRoutine = false;  // End routine (if that is what you want)
                }
        break;
        } else if (textAdd === 'backspace') {
            text.text = text.text.slice(0, -1);
            textAdd = undefined;
        } else if (textAdd == 'escape'){
            //This just prevents 'escape' being typed
            //Doesn't currently allow you to quit during the response
            textAdd = undefined; 
            break;
        } else if (textAdd !== undefined) { 
            text.text = text.text + textAdd 
            textAdd = undefined;
        }

    }
}

And at the beginning of the routine I have:

text.text = '';
psychoJS.eventManager.clearEvents();
1 Like