在Chrome浏览器上触发键盘事件

30

我正在尝试使用JavaScript在Chrome上触发页面的键盘事件。 我曾经尝试过一种方法,在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);
}

其中key是所需键,keyCode将小写字母变成大写字母并调用charCodeAt()。

我的问题是Safari / Chrome上的事件没有initKeyEvent,而是initKeyboardEvent。我能注意到的主要区别是,您必须将键作为keyIdentifier(看起来像Unicode字符)传递,而不是传递keycode和keychar。尽管如此,我仍然无法使其工作。

我还尝试了这里描述的JQuery方法,但没有成功。

编辑: 我进一步调试了一下,发现在Chrome上的事件确实会触发监听器,但keyCode/charCode始终为0。我也尝试设置evt.keyCode或evt.charCode,但也没有成功。


1
我已经将问题中的评论删除并作为答案发布,因为我认为即使它并没有真正解决问题,但对于遇到与我相同问题的人来说可能非常有帮助。 - fserb
嘿,你是怎么解决这个问题的?我也在Chrome/Safari上遇到模拟左右箭头按键的问题。但是一直找不到解决方法,你是如何做到的呢? - MaX
你试过我发布的解决方案吗?它对我有用,可能会帮助你实现你的目标,所以我觉得你接受了自己的答案“这是一个错误”很奇怪。 - Dennis
5个回答

22
我已经追踪到了一个Webkit的错误,其中创建的事件仅包含KeyIdentifier,而没有keyCode/charCode,如浏览器源代码所示。似乎正在进行修补程序来解决这个问题。所以我猜这不再是一个适当的问题...

1
人们在这里报告了使用这个美妙的黑客的成功:https://dev59.com/8Gkv5IYBdhLWcg3wpCTJ#10520017 - Paul Irish
10
自那时起发生了两件事情:1.去年我加入了Chrome团队,并将此问题分配给了自己;2.我放弃了修复此问题,因为新标准已不再支持这个功能(而且在所有平台上修复此错误非常困难)。 - fserb

11
如果您想以正确的方式完成此操作,可以使用DOM 键盘事件4级 KeyboardEvent 构造和 key 属性。
在最新的浏览器中或使用DOM键盘事件3/4级polyfill时,您可以像这样做:
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})

示例

“将事件键映射到普通QUERTY(en-US)布局的字符值”提案演示

请注意,keyCodecharCode在最新规范中已经被弃用(www.w3.org/TR/DOM-Level-3-Events/)。因此,Chrome无法使用keyCode支持initKeyEvent。但是您始终可以覆盖此值: 更新:错误的方法:

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) });
}

或者您可以更新事件原型:更新:错误的方法:

// 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;
    }
});    

更新 有各种各样的initKeyboardEvent实现。在我的KeyboardEvent polyfill中,我会像这样检测它(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
            //https://dev59.com/IljUa4cB1Zd3GeqPRGqB#8490774
            //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 );
        }
    }
}

1
你提出的两种解决方法都不可行。第一种失败是因为keyCode属性不可配置。第二种失败是因为属性描述符的get从未被调用。 - John
第一个失败是因为keyCode属性不可配置。我正在更新我的polyfill以修复各种错误和浏览器兼容性问题。 - termi

6

我只想提供这个基本片段。它在Chrome中工作,并基于Paul Irish提到的技巧。
它使用charCode代替keyCode(在某些情况下可能有用),但如果您愿意,可以适应keyCode。

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);

2

根据您的需求,TextEvent 可能适用。 (对于我在 Chrome 中的需求来说,它起作用了。 这没有进行跨浏览器测试,但问题特别涉及 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);

虽然这会触发事件,但实际上并没有插入文本。Chrome 61中的安全“功能”?-尝试一下,它不起作用https://gist.github.com/flying19880517/3180456 - mhenry1384

2
你可以使用createEvent('Event')而不是createEvent('KeyboardEvent')来避免Webkit bug,然后分配keyCode属性。请参见此答案此示例

4
两者之间有一个区别。 如果在WebKit中使用createEvent(“Events”)并向表单元素发送keyCode 13事件,则它实际上不会模拟返回键。 如果有一个监听器监听keyCode 13,它将触发该事件,但与Firefox相比,它实际上模拟了按下键盘上的回车键并提交了表单。 最近几天一直在处理这个问题,想分享一下。 :) - jxpx777

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接