在 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 += '<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 += '<script src=".."></script>;'
所以我对所有脚本重复上述代码。在 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')网格和块网格以激活相应的媒体查询断点。看到我的全部