1

我正在构建一个 Chrome 扩展程序,它在任何 HTML 页面(即在浏览器窗口中查看的任何页面)内创建一个 iFrame。

iFrame 由 Chrome 扩展的内容脚本“注入”到 HTML 页面中。生成的 HTML 如下所示:

<html>
  <head>..</head>
  <body>..</body>
  <iframe id="myIframe">...</iframe>
</html>

在内容脚本将 iFrame 添加到主页面的 DOM 后,它继续通过操作自己的 DOM 来使用内容填充 iframe。我使用 Zurb CSS 来设置 iframe 内容的样式。

这一切都很好。

我现在正在尝试将 Zurb Joyride 添加到 iframe 的内容中,而这正是我无法开始工作的地方。

我的清单的 content_scripts 声明如下所示:

"content_scripts": [{
    "matches":    ["*://*/*"],
    "js":         ["js/externalJS/jquery-2.1.4.min.js",
                   "js/externalJS/foundation.min.js",
                   "js/content_script.js"],
    "run_at":     "document_end"
}],

我怀疑 iframe 本身需要访问 Zurb 脚本,以及作为扩展的一部分由清单加载的foundation.min.js,我的内容脚本还在 iframe 中添加相关标签(通过操作 DOM )。我的理解是 iframe 中的关键元素是:

  • Zurb JS 文件的标签
  • 将兜风附加到 (id="testjoyride") 的对象
  • 兜风内容本身
  • 对foundation()的初始化调用

因此,考虑到这一点,我的清单的 web_accessible_resources 声明如下所示:

"web_accessible_resources": [

    "js/externalJS/jquery-2.1.4.min.js",
    "js/externalJS/vendor/modernizr.js",
    "js/externalJS/vendor/fastclick.js",
    "js/externalJS/foundation.min.js",
    "css/foundation.css",
    "css/app.css"
],

然后使用内容脚本在 iframe 中构建以下 HTML:

<iframe id="myIframe">
  <html>
    <head>

      <!-- the foundation style sheet (which works fine already) -->
      <link type="text/css" rel="stylesheet" href="chrome-extension://extension_id/css/foundation.css">

      <!-- the first two scripts -->
      <script src="chrome-extension://extension_id/js/externalJS/vendor/modernizr.js"></script>
      <script src="chrome-extension://extension_id/js/externalJS/jquery-2.1.4.min.js"></script>

    </head>

    <body>

      <!-- a test object to attach the joyride too -->
      <input type="submit" id="testjoyride">

      <!-- now the rest of the iframe content -->
      ... content ...

      <!-- the other two scripts -->
      <script src="chrome-extension://extension_id/js/externalJS/vendor/fastclick.js"></script>
      <script src="chrome-extension://extension_id/js/externalJS/foundation.min.js"></script>

      <!-- default joyride code from foundation.zurb.com -->
      <ol class="joyride-list" data-joyride="">  
        <li data-id="testjoyride" data-class="custom so-awesome" data-text="Next" data-prev-text="Prev">    
         <h4>Stop #1</h4>    
        </li>
        <li data-button="end" data-prev-text="Prev">
          <h4>Stop #3</h4>
        </li>
      </ol>

    </body>
  </html>
</iframe>

然后,最后,当所有上述内容都附加到 iframe(当然,iframe 附加到父页面的 DOM)时,我从 content_script 调用foundation()。

我想我需要这样称呼它: $(window.frames.myIframe.contentWindow.document).foundation('joyride', 'start');

即传入iframe的文档对象,而不是父页面**

所以我做了所有这些,但没有任何反应。兜风不会出现,HTML 也不会转换为自动生成的输出(如此所述)。

我在一个简单的 HTML 页面中创建了一个测试用例,它运行良好,我试图将该测试页面移动到 iframe 中,而joyride 停止工作。因此,尽管没有太多信心,但我怀疑问题在于 iframe,而不是 Chrome 扩展。

** 注意,我将此调用放在所有 DOM 操作的末尾。在我将我创建的新 HTML 添加到 iframe 的 DOM 之后,以及在创建我所有的侦听器等之后。所以我相信脚本和游戏应该在那时“那里”。但是,如果我在调用foundation('joyride','start') 之前粘贴一个alert(),则警报会在其后面显示一个空的iframe,并且只有在我关闭警报之后才会填充iframe。这是否重要,或者只是警报的奇怪效​​果,我不知道。

4

1 回答 1

0

感谢一些有用的评论,我设法让这个工作。完全相同的解决方案也让QTip2工作。所以这是一个解决方案:

  • 第三方 javascript 包(特别是 JQuery、Foundation Zurb Joyride 和 QTip2)...
  • ...由 Chrome 扩展程序以编程方式注入...
  • ... 到一个 iFrame - 它本身已被 Chrome 扩展程序注入到用户当前的网页中

我会尽力在下面描述它。我是自学成才,仍然是新手,因此对于任何令人困惑的术语或与最佳实践的分歧,我深表歉意。

解决方案概述

我最终为我的 Chrome 扩展程序设置了三个级别:

  1. 一个后台脚本,控制扩展(对于这个特定问题来说无关紧要,但为了完整性值得一提)。
  2. 一个内容脚本,它从后台脚本接收命令并以编程方式将 iframe、iframe HTML 内容和第三部分 JS 包注入用户的网页。
  3. 一个“iframe 脚本”,它是一个自定义 JS 脚本,它与第 3 方包一起注入到 iframe 中。这个 iframe 脚本可以向内容脚本发送/接收消息,同时可以访问第 3 方 JS 包 - Zurb Joyride 和 QTips

清单

尽管内容脚本 (content_script.js) 控制着所有的注入,但它本身从未实际运行任何第 3 方 JS 包。它们由用户的网页运行。因此我的清单的 content_scripts 声明如下所示:

"content_scripts": [{
        "matches":    ["*://*/*"],
        "js":         ["js/content_script.js"],
        "run_at":     "document_end"
    }],

因为网页需要访问第 3 方脚本,所以必须给它权限。它还需要访问我们的自定义 iframe 脚本:

"web_accessible_resources": [
        "js/iframe_script.js",

        "js/externalJS/jquery-2.1.4.min.js",
        "js/externalJS/jquery.qtip.min.js",
        "js/externalJS/vendor/modernizr.js",
        "js/externalJS/vendor/fastclick.js",
        "js/externalJS/foundation.min.js",
        "css/foundation.css",
        "css/jquery.qtip.min.css"
    ],

内容脚本:将 Joyride HTML 注入 iframe

内容脚本创建一个 iframe 元素 (id="myIframe") 并将其附加到用户的网页。然后它继续以编程方式填充内容。

其中一些内容将成为joyride 的锚点:ID 设置为joyrideStop1、joyrideStop2 等的HTML 元素:

iframeContentHTML += <i class="fi-info brandSpielIcon" id="joyrideStop1"></i>

根据Zurb 文档,内容脚本还在 iframe 主体的底部构建了joyride HTML 。

joyrideHTML += '<ol class="joyride-list" data-joyride>';
joyrideHTML += '  <li data-id="joyrideStop1" data-text="Next (1 of 5)" data-prev-text="Prev" class="customJoyride">';
joyrideHTML += '    <h4>Title</h4>';
joyrideHTML += '    <p>Content</p>';
joyrideHTML += '  </li>';
... 

我的自定义 CSS 类很简单:

.customJoyRide .joyride-nub {
    visibility: hidden;
}

这使得兜风工具提示上的小尖箭头消失了。我在试图让兜风在小 iframe 中很好地显示时遇到了很大的问题。理想情况下,我希望它们锚定到 iframe 内的元素,但显示为好像它们是主页的一部分。最后这似乎太难或不可能,所以我只是删除了小块并避开了这个问题。这是一个显示问题,并且仅适用于小型 iframe,因此不是此解决方案的核心部分。

内容脚本:将 JS 注入 iframe

为了让 Joyride 工作,我们需要将 3rd 方脚本和自定义 iframe_script.js 注入 iframe。这是第 3 方脚本之一的 content_script.js 代码示例:

// Inject foundation script into iframe body
var foundationScript = document.createElement('script');
foundationScript.src = chrome.extension.getURL('js/externalJS/foundation.min.js');
window.frames.myIframe.contentWindow.document.body.appendChild(foundationScript);

注意。这部分解决方案纠正了我最初的错误之一。如果您只是将文本附加到 innerHTML ,脚本似乎不会初始化,如下所示:body.innerHTML += '<script src=".."></script>;'

所以我对所有脚本重复上面的代码。在 iframe 头中我放了:

  • jquery.qtipmin.css
  • Modernizr.js
  • jquery-2.1.4.min.js

在 iframe 主体的底部,我放了:

  • 快速点击.js
  • 基础.min.js
  • jquery.qtip.min.js
  • iframe_script.js。

以该顺序。

iframe 脚本:初始化

当 iframe_script.js 初始化时,我立即让它添加一个消息侦听器(以便内容脚本可以与之通信),然后向内容脚本发送一条消息,告诉它它已准备好(内容脚本已经设置了自己的消息侦听器,这里不详述代码):

(function initialise(){

    // Listen for messages from the content script
    window.addEventListener("message", contentScriptMessage_listener, false);

    // Tell the content script we're ready to go
    window.parent.postMessage({sender:  'iframe_script',
                               subject: 'iframeScriptHasInitialised'}, '*');
})();

注意。我努力确保在我的 content_script 向 iframe 发送第一条消息之前设置了 iframe 的侦听器。最后这个解决方案(等待来自 iframe 的消息)似乎效果最好,尽管我怀疑可能有更好的方法

内容脚本:告诉 iframe 激活 Joyride

现在内容脚本知道 iframe 侦听器已准备就绪,它可以告诉它激活 Zurb Joyride(和/或 QTips)。我通过将消息发送回 iframe 来做到这一点。

window.frames.myIframe.contentWindow.postMessage({
    sender:  'content_script', 
    subject: 'pleaseActivateJoyride'}, '*');

显然,直接从 iframe 脚本激活 Joyride 会更简单,而无需所有这些消息来回弹跳。但这就是重点:内容脚本知道是否应该显示兜风,更不用说所有其他配置细节了。使用内容脚本(最终使用后台脚本)保持控制可以使整体实现更加整洁。

iframe 脚本:激活 Joyride

我的 iframe 脚本的消息处理程序从内容脚本接收消息并调用此函数:

function activateJoyride(){

    $(document).foundation({
        joyride: {
            ... configuration ...
        }
    });

    $(document).foundation('joyride', 'start');
}

注意。我在这里有一个悬而未决的问题。这有时会在加载 JQuery 之前运行,从而导致错误(“$ 未声明”)。我玩过 window.setTimeout 来解决这个问题,它几乎一直都在工作......但它不是 100%

就是这样!

QTips 有一个匹配的激活函数,适当的内容更早地注入到 iframe 中。就像兜风一样,我在小 iframe 内渲染 Qtips 时遇到了困难。QTip2 更强大,文档建议有解决方案

于 2015-09-10T23:10:54.050 回答