document.activeElement仅适用于Gmail的firefox插件内容脚本

Nav*_*K N 12 javascript firefox firefox-addon firefox-addon-sdk

我正在使用addon-sdk开发一个Firefox插件.此插件将菜单项添加到上下文菜单,用户可以右键单击任何编辑控件以激活此菜单项.一旦激活,它会在用户输入内容时显示一个带有建议的小弹出窗口.

除了Gmail之外,一切运作良好.

在Gmail中,以下代码失败.

self.port.on('showPopup', function(data) {
    var active = document.activeElement;
    console.log(active.type);
    if (active && getWordUnderCaret(active).word == data.input) {
        populateSuggestions(data);
        positionPopup(active);
        stylePopup();
    }
});
Run Code Online (Sandbox Code Playgroud)

失败的原因是document.activeElement指向document.bodygetWordUnderCaret失败,因为它期望输入/ textarea控制.这适用于所有其他地方.我不确定它为什么指向,document.body因为我可以看到焦点在于输入控件.键入document.activeElementFirebug控制台给了我适当的对象.

或者,我试图自己跟踪活动元素而不是使用document.activeElement.但我遇到了一些问题,比如坚持这个问题.我不能用window它作为window代理来坚持这个.我尝试过,unsafeWindow但无法让它工作.

我想知道为什么这在Gmail中失败了?任何帮助解决这个问题都会很棒!

我的代码可以在Github上找到

编辑

看起来这是addon-sdk的一个问题.我创建了一个Gist,可以用来重现问题.它可以在这里找到

Onu*_*rım 12

我想,你指的是Gmail上的大框,你在撰写邮件时输入邮件正文.

»如果是这样的话;

原因:

Gmail上的编辑控件不是文本控件(输入或文本区域).这是一个WYSIWYG编辑器,通过设置(或 取决于浏览器支持)<iframe>进行编辑.document.designMode = "on"document.body.contentEditable = true

所以,点击时; document.activeElement在这种情况下,您并不总是以查询的方式获得正确的元素.你得到的包装(主)文件的body,(甚至没有iframebody).

例如; 我相信,你的代码的这一行 ; 您将一个click事件侦听器添加到主文档.但您也应该将其添加到页面中的可编辑iframe中; 因为页面和iframe具有不同的document对象(并且iframe不会将其自身设置为活动对象).所以,你会得到错误的document.activeElement.

解决方法:

将必要的事件侦听器添加到iframe中; 因为你想得到关于他们的通知.

// Add mouseup event listener to the main document
document.addEventListener('mouseup', logActiveElement, false);
// Get all the iframes on the main document.
var iframeElems = document.getElementsByTagName('iframe');
// Add mouseup event listener to the document of the iframe elements
for (var i = 0; i < iframeElems.length; ++i) {
    addIframeEvent(iframeElems[i], true, 'mouseup', logActiveElement);
}
Run Code Online (Sandbox Code Playgroud)

以下是帮助函数:(
注意我们如何从主文档向iframe(文档)添加事件侦听器以及我们如何检查iframe当前是否可编辑.)

// Adds the specified event listener to the iframe element. 
function addIframeEvent(iframeElem, editableOnly, eventName, callback) {
    // Get the document of the iframe element.
    var iframeDocument = iframeElem.contentDocument || 
                            iframeElem.contentWindow.document;
    // Watch for editableOnly argument. 
    if ( (editableOnly && isDocEditable(iframeDocument)) || !editableOnly) {
        // Add the event listener to the document of the iframe element.
        iframeDocument.addEventListener(eventName, callback, false);
    }
}

// Checks whether the specified document is content-editable or in design mode.
function isDocEditable(doc) {
    return ( ('contentEditable' in doc.body) && (doc.body.contentEditable === true) ) || 
        ('designMode' in doc) && (doc.designMode == "on") );
}

// Handler function
function logActiveElement() {
    console.log("Active Element:", document.activeElement);
}
Run Code Online (Sandbox Code Playgroud)

现在,事件处理程序将正确的日志记录activeElement到控制台.

并发症:

结果是; 现有代码中的文本选择和检查等功能与常规编辑控件(输入,文本区域等)的工作方式不同.

例如; 你的getWordUnderCaret(editor)功能得到insertionPointeditor.selectionStart不中不存在document.body(的目标iframe).对于这种选择/检查; 你应该在DOM选择,DOM范围之间切换; 而不是文本编辑控件中的文本选择和范围.

注意:您使用的文本选择/范围jQuery库(Rangy)承诺在浏览器中处理这种可编辑的内容(iframe,div等).您是否尝试过iframe(例如在Gmail上)?

更多的信息:

  • 请参阅Mozilla Developer Network上的activeElement文档.另外,它表示:"不要将焦点与文档上的选择混淆.当没有选择时, activeElement页面就是页面<body>."

更新:

检查Gmail上的文本控件; 我也玩过你的示例代码;

  • 添加attachTo: ["existing", "top", "frame"]到PageMod选项.
  • 在PageMod选项中更改contentScriptWhen'end'(而不是 'ready')的值; 确保包括DOM,CSS,JS在内的所有内容都已加载.某些内容可能会在页面就绪/加载时通过JS进行更改,因此"结束"将在其后执行.
  • 将选择器上下文也应用于菜单项; 以确保项目仅在选择器上下文中出现.

在Gmail(搜索框,聊天框等)和其他网站上测试; 这似乎有效.
见/测试工作的例子在这里的附加建设者.

Gmail搜索框文本输入

Gmail聊天框textarea