将 Greasemonkey/Tampermonkey 用户脚本应用于 iframe
Apply a Greasemonkey/Tampermonkey userscript to an iframe
如何在 iframe 中运行制作用户脚本?
在 Greasemonkey(以及 Tampermonkey 和大多数用户脚本引擎)中,如果满足 @include, @exclude, and/or @match 指令,脚本将在 iframe 上自动触发。
而且,一个流行的问题是 how to stop Greasemonkey from firing on iframes.
因此,如果您的脚本匹配如下:
@match https://fiddle.jshell.net/*
无论它们是否出现在 iframe 中,它都会在 jsFiddle“输出”页面上触发。
如果您想在纯 iframed 内容上触发:
然后你会检查 window.self
属性.
例如,假设您的目标页面如下:
<body>
<h1>I'm some webpage, either same-domain or not.</h1>
<iframe src="//domain_B.com/somePath/somePage.htm">
...
然后您可以使用如下脚本:
// ==UserScript==
// @name _Fires specially on domain_B.com iframes
// @match *://domain_B.com/somePath/*
// ==/UserScript==
if (window.top === window.self) {
//--- Script is on domain_B.com when/if it is the MAIN PAGE.
}
else {
//--- Script is on domain_B.com when/if it is IN AN IFRAME.
// DO YOUR STUFF HERE.
}
重要:
随着 Greasemonkey 4, iframes handling is severely crippled 的发布(除此之外,还有很多其他东西被破坏了)。
它 仍然可以与 Tampermonkey、Violentmonkey 和 几乎所有其他用户脚本引擎一起正常工作。
强烈建议 (including by Greasemonkey itself) 您不要使用 Greasemonkey 4 或更高版本。
请注意,如果您要为您的用户脚本制作 chrome 扩展,您还需要将 "all_frames": true
添加到您的清单中,否则您的扩展将无法在 iframe 上运行。
清单文件中的示例:
"content_scripts": [
{
"matches": ["*://*/*"],
"all_frames": true,
"js":["dont.js"],
"run_at":"document_start"
}
]
这是针对 iframe
没有位置触发 @include
或 @match
的情况的解决方案。
这适用于 Greasemonkey 4。
我们必须等待每一帧加载完毕才能对其进行操作。我通过使用 waitForKeyElements.js
, which waits for elements matching a given CSS selector, just like looping through the matches in document.querySelectorAll("selector")
来做到这一点,然后将给定的函数应用于响应:
// ==UserScript==
// @include https://blah.example.com/*
// @require https://git.io/waitForKeyElements.js
// ==/UserScript==
function main(where) {
// do stuff here with where instead of document
// e.g. use where.querySelector() in place of document.querySelector()
// and add stylesheets with where.head.appendChild(stylesheet)
}
main(document); // run it on the top level document (as normal)
waitForKeyElements("iframe, frame", function(elem) {
elem.removeAttribute("wfke_found"); // cheat wfke's been_there, use our own
for (let f=0; f < frames.length; f++) {
if (!frames[f].document.body.getAttribute("been_there")) {
main(frames[f].document);
frames[f].document.body.setAttribute("been_there", 1);
}
}
});
请注意,所选元素只是一个占位符,表示 iframe
已加载。我们从 waitForKeyElements
中删除了 "been there" 跟踪器,因为稍后可能会再次加载框架(我们不能只使用那个 iframe
,因为它的内容已在其他地方加载)。
当我们知道一个框架已经加载时,我们遍历每个框架并寻找我们的标记,框架 body
中的一个 HTML 属性称为 been_there
(如 <body been_there="1">
).如果它丢失了,我们可以 运行 框架文档上的 main()
函数。完成后,我们添加 been_there
属性,这样就不会再次触发。
中的解决方案对我不起作用,因此我对其进行了一些更改。它适用于 Greasemonkey 4.10。
// ==UserScript==
// @include https://blah.example.com/*
// @require https://git.io/waitForKeyElements.js
// ==/UserScript==
function main(where) {
// do stuff here with where instead of document
// e.g. use where.querySelector() in place of document.querySelector()
// and add stylesheets with where.head.appendChild(stylesheet)
}
main(document); // run it on the top level document (as normal)
waitForKeyElements("iframe, frame", function(elem) {
elem.addEventListener("load", function () {
elem.removeAttribute("wfke_found");
});
main(elem.contentDocument);
});
最大的变化是它现在甚至在浏览 iframe 时也能正常工作。
iframe.contentWindow.document
允许在 FireMonkey 中获取 iframe 内容,例如:
const iframeDocument = document.getElementById('iframe_id').contentWindow.document;
const iframeAnchors = iframeDocument.evaluate(
'//a',
iframeDocument,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null);
如何在 iframe 中运行制作用户脚本?
在 Greasemonkey(以及 Tampermonkey 和大多数用户脚本引擎)中,如果满足 @include, @exclude, and/or @match 指令,脚本将在 iframe 上自动触发。
而且,一个流行的问题是 how to stop Greasemonkey from firing on iframes.
因此,如果您的脚本匹配如下:
@match https://fiddle.jshell.net/*
无论它们是否出现在 iframe 中,它都会在 jsFiddle“输出”页面上触发。
如果您想在纯 iframed 内容上触发:
然后你会检查 window.self
属性.
例如,假设您的目标页面如下:
<body>
<h1>I'm some webpage, either same-domain or not.</h1>
<iframe src="//domain_B.com/somePath/somePage.htm">
...
然后您可以使用如下脚本:
// ==UserScript==
// @name _Fires specially on domain_B.com iframes
// @match *://domain_B.com/somePath/*
// ==/UserScript==
if (window.top === window.self) {
//--- Script is on domain_B.com when/if it is the MAIN PAGE.
}
else {
//--- Script is on domain_B.com when/if it is IN AN IFRAME.
// DO YOUR STUFF HERE.
}
重要:
随着 Greasemonkey 4, iframes handling is severely crippled 的发布(除此之外,还有很多其他东西被破坏了)。
它 仍然可以与 Tampermonkey、Violentmonkey 和 几乎所有其他用户脚本引擎一起正常工作。
强烈建议 (including by Greasemonkey itself) 您不要使用 Greasemonkey 4 或更高版本。
请注意,如果您要为您的用户脚本制作 chrome 扩展,您还需要将 "all_frames": true
添加到您的清单中,否则您的扩展将无法在 iframe 上运行。
清单文件中的示例:
"content_scripts": [
{
"matches": ["*://*/*"],
"all_frames": true,
"js":["dont.js"],
"run_at":"document_start"
}
]
这是针对 iframe
没有位置触发 @include
或 @match
的情况的解决方案。
这适用于 Greasemonkey 4。
我们必须等待每一帧加载完毕才能对其进行操作。我通过使用 waitForKeyElements.js
, which waits for elements matching a given CSS selector, just like looping through the matches in document.querySelectorAll("selector")
来做到这一点,然后将给定的函数应用于响应:
// ==UserScript==
// @include https://blah.example.com/*
// @require https://git.io/waitForKeyElements.js
// ==/UserScript==
function main(where) {
// do stuff here with where instead of document
// e.g. use where.querySelector() in place of document.querySelector()
// and add stylesheets with where.head.appendChild(stylesheet)
}
main(document); // run it on the top level document (as normal)
waitForKeyElements("iframe, frame", function(elem) {
elem.removeAttribute("wfke_found"); // cheat wfke's been_there, use our own
for (let f=0; f < frames.length; f++) {
if (!frames[f].document.body.getAttribute("been_there")) {
main(frames[f].document);
frames[f].document.body.setAttribute("been_there", 1);
}
}
});
请注意,所选元素只是一个占位符,表示 iframe
已加载。我们从 waitForKeyElements
中删除了 "been there" 跟踪器,因为稍后可能会再次加载框架(我们不能只使用那个 iframe
,因为它的内容已在其他地方加载)。
当我们知道一个框架已经加载时,我们遍历每个框架并寻找我们的标记,框架 body
中的一个 HTML 属性称为 been_there
(如 <body been_there="1">
).如果它丢失了,我们可以 运行 框架文档上的 main()
函数。完成后,我们添加 been_there
属性,这样就不会再次触发。
// ==UserScript==
// @include https://blah.example.com/*
// @require https://git.io/waitForKeyElements.js
// ==/UserScript==
function main(where) {
// do stuff here with where instead of document
// e.g. use where.querySelector() in place of document.querySelector()
// and add stylesheets with where.head.appendChild(stylesheet)
}
main(document); // run it on the top level document (as normal)
waitForKeyElements("iframe, frame", function(elem) {
elem.addEventListener("load", function () {
elem.removeAttribute("wfke_found");
});
main(elem.contentDocument);
});
最大的变化是它现在甚至在浏览 iframe 时也能正常工作。
iframe.contentWindow.document
允许在 FireMonkey 中获取 iframe 内容,例如:
const iframeDocument = document.getElementById('iframe_id').contentWindow.document;
const iframeAnchors = iframeDocument.evaluate(
'//a',
iframeDocument,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null);