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

时间:2013-01-11 05:40:44

标签: 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();
    }
});

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

或者,我尝试自己跟踪活动元素,而不是使用document.activeElement。但我遇到了一些问题,比如坚持这个问题。由于window是代理,我无法使用window来保留此信息。我尝试使用unsafeWindow但无法使其正常工作。

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

我的代码位于Github

修改

看起来这是addon-sdk的一个问题。我创建了一个Gist,可以用来重现问题。它可用here

2 个答案:

答案 0 :(得分:12)

我想,您指的是Gmail上的大框,您在撰写电子邮件时会在其中输入邮件正文。

»如果是这样的话;

原因:

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

所以,点击时;在这种情况下,您并不总是按照查询 document.activeElement 的方式获取正确的元素。你得到了包装(主要)文档的body,(甚至不是iframe的{​​{1}})。

例如;我相信,at this line你的代码;您将一个body事件侦听器添加到主文档。但您还应将其添加到页面中的可编辑iframe ;因为页面和iframe具有不同的click个对象(并且iframe不会将其自身设置为活动状态)。因此,您将得到错误的 document

解决方法:

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

document.activeElement

以下是辅助功能:
(请注意我们如何从主文档向iframe(文档)添加事件侦听器,以及我们如何检查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);
}

现在,事件处理程序会将正确的// 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); } 记录到控制台。

并发症:

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

例如;您的activeElement函数获得getWordUnderCaret(editor) insertionPoint editor. selectionStart document.body(目标iframe中不存在})。对于这种选择/检查;你应该在DOM SelectionsDOM Ranges之间切换;而不是text edit control中的文本选择和范围。

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

更多信息:

  • 请参阅Mozilla Developer上的 activeElement documentation 网络。另外,它表明: “不要把焦点与a混淆 选择文件。当没有选择时, activeElement是页面的<body>。“

UPDATE:

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

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

在Gmail(搜索框,聊天框等)和其他网站上测试;这似乎有效。
查看/测试工作example here on addon builder

Gmail Search-box text input

Gmail Chat-box textarea

答案 1 :(得分:3)

您是否尝试过使用addon sdk的最新git版本?那里的上下文菜单已经从头开始重写,我听说它修复了很多错误。