在 iFrame 中激活 Zurb Foundation 的 Joyride,来自 Chrome 扩展内容脚本

Posted

技术标签:

【中文标题】在 iFrame 中激活 Zurb Foundation 的 Joyride,来自 Chrome 扩展内容脚本【英文标题】:Activating Zurb Foundation's Joyride in an iFrame, from Chrome Extension content script 【发布时间】:2015-12-03 01:18:26 【问题描述】:

我正在构建一个 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 也不会转换为自动生成的输出(如 here 所述)。

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

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

【问题讨论】:

暗示您在内容脚本中创建 iframe 尝试使用 window.postMessage (example) 向您的 iframe 脚本发送事件,然后在 iframe 中注入一个额外的简单脚本,并为此添加一个侦听器将调用foundation('joyride', 'start'); 的消息。或者使用 chrome API 来交换消息,但这可能会比较麻烦。 就是这样!我不明白的三件事:1)正如你所说,@wOxxOm,从 iframe 调用 foundation('joyride','start') 就是答案。但是为什么从内容脚本(传入 iframe 文档)调用它不起作用? 2) 如果我只使用innerHTML += '&lt;script src='...,将外部脚本添加到 iframe 中不起作用。我必须使用document.createElement('script'),然后使用appendChild() 将其添加到DOM。 3) 在调用 postMessage() 之前,我必须强制延迟内容脚本。我使用window.timeout(),即使延迟设置为 0,它也可以工作。这看起来很奇怪。 如果您将其发布为详细答案会很好,因为我只提供了一个提示并且无法测试代码。至于您的问题: 1. 因为每个文档(iframe 是一个文档)在其自己的沙箱中运行代码 2. idk,我从不使用 += 用于 innerHTML,因为我认为它非常糟糕 3. setTimeout(..., 0) 将代码排队一个单独的事件任务/作业,在任何其他排队之后执行,包括 DOM 更新。 @wOxxOm 感谢您对这三点的反馈,更不用说首先解决问题了。这是一个巨大的帮助,真的很感激。我会尽快给出详细的答复。但这是否意味着我不给你任何“提示”投票?毕竟,这是我做错的核心...... 将信息汇总到一个好的答案中是一项比幸运猜测更大的任务,因此您可以继续进行,因为您已经完成了与我不同的测试。 【参考方案1】:

感谢一些有用的 cmets,我成功地完成了这项工作。完全相同的解决方案也让QTip2 工作。所以这是一个解决方案:

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

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

解决方案概述

我的 Chrome 扩展程序最终得到了三个级别:

    一个后台脚本,控制扩展(对于这个特定问题相当无关紧要,但为了完整性值得一提)。 内容脚本,从后台脚本接收命令并以编程方式将 iframe、iframe HTML 内容和第三部分 JS 包注入用户的网页。 “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>

内容脚本还根据 iframe 主体底部的 Zurb docs 构建 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 += '&lt;script src=".."&gt;&lt;/script&gt;;'

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

jquery.qtipmin.css modernizr.js jquery-2.1.4.min.js

在 iframe 主体的底部我放了:

fastclick.js foundation.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 激活兜风

现在内容脚本知道 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 更强大,文档建议有solutions。

【讨论】:

以上是关于在 iFrame 中激活 Zurb Foundation 的 Joyride,来自 Chrome 扩展内容脚本的主要内容,如果未能解决你的问题,请参考以下文章

scss 我重新调整了Zurb 4.3 / 5(主要是添加'medium')网格和块网格以激活相应的媒体查询断点。看到我的全部

scss 我重新调整了Zurb 4.3 / 5(主要是添加'medium')网格和块网格以激活相应的媒体查询断点。看到我的全部

scss 我重新调整了Zurb 4.3 / 5(主要是添加'medium')网格和块网格以激活相应的媒体查询断点。看到我的全部

scss 我重新调整了Zurb 4.3 / 5(主要是添加'medium')网格和块网格以激活相应的媒体查询断点。看到我的全部

scss 我重新调整了Zurb 4.3 / 5(主要是添加'medium')网格和块网格以激活相应的媒体查询断点。看到我的全部

scss 我重新调整了Zurb 4.3 / 5(主要是添加'medium')网格和块网格以激活相应的媒体查询断点。看到我的全部