27
votes

I'm trying to fire a keyboard event to a page using javascript on Chrome. I had an approach that used to work on Firefox:

pressKey = function(key, shift) {
  var evt = document.createEvent('KeyboardEvent');
  evt.initKeyEvent("keypress", false, true, null, false, false,
                   shift, false, keyCode(key), key.charCodeAt(0));
  document.dispatchEvent(evt);
}

where key is the desired key and keyCode changes lowercase letters into highercase and also calls charCodeAt().

My problem is that events on Safari/Chrome don't have initKeyEvent, but initKeyboardEvent. The main difference I could notice was that you have to pass the key as a keyIdentifier (which looks like a unicode character) instead of passing the keycode and the keychar. Nonetheless I still can't manage to make it work.

I've also tried the JQuery approach described here without success.

EDIT: I debugged this a little further and it seems that the event on Chrome does trigger the listeners, but keyCode/charCode is always 0. I've tried to set evt.keyCode or evt.charCode with no success either.

5
I've removed a comment from the question and put as answer, because I think that even thought it doesn't really solve the question, it might be really helpful for people having the same problem as I did.fserb
Hey how did you solve the problem? I am also facing issue of simulating key-press of left and right on chrome/safari. But no success, how did you do it?MaX
Did you try the solution I posted? It worked for me and would probably have helped you achieve your goal, so it's odd to me that you accepted your own answer of "it's a bug".Dennis

5 Answers

5
votes

I just want to throw this basic snippet out there. It works in Chrome and is based on the hack mentioned by Paul Irish.
It uses charCode instead of keyCode (which can be useful in certain situation), but adapt to keyCode if you so please.

var keyboardEvent = new KeyboardEvent('keypress', {bubbles:true}); 
Object.defineProperty(keyboardEvent, 'charCode', {get:function(){return this.charCodeVal;}}); 
keyboardEvent.charCodeVal = [your char code];
document.body.dispatchEvent(keyboardEvent);
19
votes

I've tracked this down to a bug on Webkit where created events only contain KeyIdentifier but no keyCode/charCode as can be seen on the browser source code. There seems to be a patch going on to solve this. So I guess this is not a proper question anymore...

11
votes

If you want do it in the right way, you can use DOM Keyboard Event Level 4 KeyboardEvent construct and key property.

In latest browsers or with DOM Keyboard Event Level 3/4 polyfill you can do something like this:

element.addEventListener(function(e){ console.log(e.key, e.char, e.keyCode) })

var e = new KeyboardEvent("keydown", {bubbles : true, cancelable : true, key : "Q", char : "Q", shiftKey : true});
element.dispatchEvent(e);

//If you need legacy property "keyCode".
// Note: In some browsers you can't overwrite "keyCode" property. (At least in Safari)
delete e.keyCode;
Object.defineProperty(e, "keyCode", {"value" : 666})

Example

"map event.key to character values of a normal QUERTY (en-US) layout" proposal demo

Note that keyCode and charCode are deprecated in latest Spec (www.w3.org/TR/DOM-Level-3-Events/). So the is no chance to Chrome to implement initKeyEvent with keyCode support. But you can always override this value: UPDATE: bad method:

var evt = document.createEvent('KeyboardEvent');
evt.initKeyEvent("keypress", false, true, null, false, false,
               shift, false, keyCode(key), key.charCodeAt(0));
if(evt.keyCode != keyCode(key)) {
    delete evt.keyCode;
    // Note: In some browsers you can't overwrite "keyCode" property. (At least in Safari)
    Object.defineProperty(evt, "keyCode", { keyCode(key) });
}

Or you can update event prototype: UPDATE: bad method:

// Note: In some browsers you can't overwrite "keyCode" property. (At least in Safari)
var _native_keyCode_getter = Object.getOwnPropertyDescriptor(KeyboardEvent.prototype, "keyCode");
Object.defineProperty(KeyboardEvent.prototype, "keyCode", {
    "enumerable" : true,
    "configurable" : true,
    "get" : function() {
        if("__keyCode" in this)return this["__keyCode"];

        return _native_keyCode_getter.call(this);
    },
    "set" : function(newValue) {
        return this["__keyCode"] = isNaN(newValue) ? 0 : newValue;
    }
});    

Update There are various implementation of initKeyboardEvent. In my KeyboardEvent polyfill I detect it somehow like this (gist):

var _initKeyboardEvent_type = (function( e ) {
    try {
        e.initKeyboardEvent(
            "keyup" // in DOMString typeArg
            , false // in boolean canBubbleArg
            , false // in boolean cancelableArg
            , global // in views::AbstractView viewArg
            , "+" // [test]in DOMString keyIdentifierArg | webkit event.keyIdentifier | IE9 event.key
            , 3 // [test]in unsigned long keyLocationArg | webkit event.keyIdentifier | IE9 event.location
            , true // [test]in boolean ctrlKeyArg | webkit event.shiftKey | old webkit event.ctrlKey | IE9 event.modifiersList
            , false // [test]shift | alt
            , true // [test]shift | alt
            , false // meta
            , false // altGraphKey
        );
        return ((e["keyIdentifier"] || e["key"]) == "+" && (e["keyLocation"] || e["location"]) == 3) && (
            e.ctrlKey ?
                e.altKey ? // webkit
                    1
                    :
                    3
                :
                e.shiftKey ?
                    2 // webkit
                    :
                    4 // IE9
            ) || 9 // FireFox|w3c
            ;
    }
    catch ( __e__ ) { alert("browser do not support KeyboardEvent") }
})( document.createEvent( "KeyboardEvent" ) );

var e = document.createEvent( "KeyboardEvent" );
...
if( "initKeyEvent" in e ) {//FF
    //https://developer.mozilla.org/en/DOM/event.initKeyEvent
    e.initKeyEvent( type, _bubbles, _cancelable, _view, _ctrlKey, _altKey, _shiftKey, _metaKey, _keyCode, _keyCode );
}
else if( "initKeyboardEvent" in e ) {//https://developer.mozilla.org/en/DOM/KeyboardEvent#initKeyboardEvent()
    if( _try_initKeyboardEvent ) {
        if( _initKeyboardEvent_type == 1 ) { // webkit
            //http://stackoverflow.com/a/8490774/1437207
            //https://bugs.webkit.org/show_bug.cgi?id=13368
            e.initKeyboardEvent( type, _bubbles, _cancelable, _view, _key, _location, _ctrlKey, _shiftKey, _altKey, _metaKey, _altGraphKey );
        }
        else if( _initKeyboardEvent_type == 2 ) { // old webkit
            //http://code.google.com/p/chromium/issues/detail?id=52408
            e.initKeyboardEvent( type, _bubbles, _cancelable, _view, _ctrlKey, _altKey, _shiftKey, _metaKey, _keyCode, _keyCode );
        }
        else if( _initKeyboardEvent_type == 3 ) { // webkit
            e.initKeyboardEvent( type, _bubbles, _cancelable, _view, _key, _location, _ctrlKey, _altKey, _shiftKey, _metaKey, _altGraphKey );
        }
        else if( _initKeyboardEvent_type == 4 ) { // IE9
            //http://msdn.microsoft.com/en-us/library/ie/ff975297(v=vs.85).aspx
            e.initKeyboardEvent( type, _bubbles, _cancelable, _view, _key, _location, _modifiersListArg, _repeat, _locale );
        }
        else { // FireFox|w3c
            //http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent-initKeyboardEvent
            //https://developer.mozilla.org/en/DOM/KeyboardEvent#initKeyboardEvent()
            e.initKeyboardEvent( type, _bubbles, _cancelable, _view, _char, _key, _location, _modifiersListArg, _repeat, _locale );
        }
    }
}
2
votes

Depending on your needs, a TextEvent might work. (It worked for me for my needs - for chrome. This is not cross-browser tested, but then, the question was specifically about chrome.)

// get a reference to the DOM element you want to affect
var input = document.getElementsByTagName('input')[0];
// create a TextEvent
var textEvent = document.createEvent('TextEvent');
// initialize the TextEvent
textEvent.initTextEvent('textInput', true, true, null, String.fromCharCode(13)+"\r\n", 9, "en-US");
// dispatch ('fire') the TextEvent
input.dispatchEvent(textEvent);
2
votes

You can work around the Webkit bug using createEvent('Event') rather than createEvent('KeyboardEvent'), and then assigning the keyCode property. See this answer and this example.