【问题标题】:How can I detect keyboard events in Gmail如何检测 Gmail 中的键盘事件
【发布时间】:2021-08-10 21:45:32
【问题描述】:

我正在编写一个浏览器扩展,它需要将处理程序附加到所有页面上的keyupkeydown 事件。我可以使用以下内容脚本代码使其正常工作。

document.addEventListener("keydown",keyDown, true);      
document.addEventListener("keyup", keyUp, true);

我无法让它在 Gmail 中工作。具体来说,在撰写新电子邮件的正文时,我无法让它工作。它适用于我测试过的其他任何地方。我认为问题在于 Gmail 在所有键盘事件上都调用了stopPropagation,但很难调试它们的最小化代码。我认为将第三个参数设置为true 会导致在CAPTURE_PHASE 期间捕获事件,但这不起作用。

如何在使用 Google Chrome 内容脚本在 Gmail 中编写新正文时捕获 keyupkeydown 事件?

编辑:

通过将"all_frames": true, 添加到我的清单,我确保我的内容脚本被注入到 DOM 的所有 iframe 中。我什至尝试过使用以下代码:

document.addEventListener("DOMNodeInserted", function (event) {
     if(event.type === "DOMNodeInserted") {
        if(event.srcElement.nodeName === "IFRAME") {
        console.log(event.srcElement.nodeName + " iframe detected");
        event.srcElement.addEventListener("keydown", function(kevent) {
            document.dispatchEvent(kevent);
            }, true);
        event.srcElement.addEventListener("keyup", function(kevent) {
            document.dispatchEvent(kevent);
            }, true);
        
    }
}
},true);

这仍然不能解决 Gmail 的问题。

【问题讨论】:

  • Gmail 也往往有很多 iframe。确保您正在侦听正确事件中的事件。
  • 我已确保将我的内容脚本注入到所有 iframe 中。我什至尝试过一个构建,当 iframe 被添加到 DOM 时我检测到:(见编辑)

标签: javascript google-chrome-extension gmail dom-events


【解决方案1】:

您的代码不起作用,因为event.srcElement 指的是<iframe> 元素,而不是其内容。要访问其内容文档,您必须等待框架加载(onload 或轮询),然后使用frame.contentDocument 访问框架。

从 Chrome 37.0.1995.0 开始,您还可以使用 match_about_blank(与 all_frames)在 about:blank 框架中插入内容脚本,以捕获事件并将其发送到父内容脚本。

这里是原始想法的实现示例(使用轮询):

manifest.json的相关部分:

  "content_scripts": [{
      "matches": ["*://mail.google.com/*"],
      "js": ["contentscript.js"],
      "run_at": "document_end"
  }],

contentscript.js

function keyDown(e) {console.log(e.which);}; // Test
function keyUp(e) {console.log(e.keyCode);}; // Test
(function checkForNewIframe(doc) {
    if (!doc) return; // document does not exist. Cya

    // Note: It is important to use "true", to bind events to the capturing
    // phase. If omitted or set to false, the event listener will be bound
    // to the bubbling phase, where the event is not visible any more when
    // Gmail calls event.stopPropagation().
    // Calling addEventListener with the same arguments multiple times bind
    // the listener only once, so we don't have to set a guard for that.
    doc.addEventListener('keydown', keyDown, true);
    doc.addEventListener('keyup', keyUp, true);
    doc.hasSeenDocument = true;
    for (var i = 0, contentDocument; i<frames.length; i++) {
        try {
            contentDocument = iframes[i].document;
        } catch (e) {
            continue; // Same-origin policy violation?
        }
        if (contentDocument && !contentDocument.hasSeenDocument) {
            // Add poller to the new iframe
            checkForNewIframe(iframes[i].contentDocument);
        }
    }
    setTimeout(checkForNewIframe, 250, doc; // <-- delay of 1/4 second
})(document); // Initiate recursive function for the document.

请注意,我使用轮询而不是 DOM 突变事件,因为后者 heavily reduces performance

【讨论】:

  • 我用这段代码做了一个单独的测试扩展,在输入 gmail 电子邮件文档的正文时它不起作用。
  • @ZeroDivide Gmail 的电子邮件正文是框架内的框架。我已经用一个递归的、非冲突的函数来更新我的答案来添加事件。
  • @ZeroDivide 我添加了这个长期轮询以考虑稍后添加的元素。创建新消息时,会创建一个新的 iframe。使用DOMNodeInserted 来检测这会显着降低性能。轮询是更好的解决方案。如果立即拥有新 iframe 的侦听器并不重要,请增加延迟。我添加了这些数字以确保不使用该变量。我使用了rw,因为这就是我为自己的自定义变量添加前缀的方式。这些数字等于您问题的 postId。结合在一起,我得到了一个非常独特的描述变量。
  • @DavidJeske 请在您发表评论之前花点时间阅读这三个 cmets(这里有一个链接让它更明显:SO Q&A: "Unsafe JavaScript attempt to access frame with URL https://plus.google.com/u/0/_/...")。
  • @RobW 按键捕获阶段参数对我来说很重要。正确设置后,无需使用 all_frames=true 或循环遍历所有 iframe 文档,即可为 gmail 撰写窗口中的键触发 keypress 事件。
猜你喜欢
  • 1970-01-01
  • 2010-09-18
  • 2019-01-24
  • 1970-01-01
  • 2017-04-06
  • 1970-01-01
  • 2014-06-22
  • 1970-01-01
相关资源
最近更新 更多