如何让 GreaseMonkey 脚本在页面元素显示之前影响它们?
How to make a GreaseMonkey script affect elements in a page before they're displayed?
我试图确保不显示某个网站中的图片,但仍然显示替代文字。最初我试图用 Stylish(使用 Firefox)完成这个并问了以下问题:
接受的答案为我提供了使用 Greasemonkey 的替代解决方案。该脚本使用 waitForKeyElements 来隐藏图像,即使它们是使用 AJAX 添加的。
我将给定的脚本更改为以下内容:
// ==UserScript==
// @name _Hide pics except for alt text
// @include http://YOUR_SERVER.COM/YOUR_PATH/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// ==/UserScript==
GM_addStyle ( " \
* { \
background-image: none !important; \
} \
" );
waitForKeyElements ("img", hideImageExceptForAltText);
function hideImageExceptForAltText (jNode) {
var imgAlt = jNode.attr("alt");
var imgTitle = jNode.attr("title");
jNode.css("display", "none");
var newSpan = $("<span></span>");
newSpan.attr("title", imgTitle);
newSpan.append(imgAlt);
jNode.parent().append(newSpan);
}
就像原始脚本一样,这有一个问题,即在页面加载时图像仍然会显示片刻。
是否可以确保给定的函数将阻止页面上的图像立即显示,以便它们根本不可见?
编辑:Brock Adams 的回复提供了我遗漏的线索。如果有人正在寻找这样的东西,以下是我最终使用的。它在我需要它的网站上运行良好,但我不能保证它在除 Firefox 之外的其他网站或其他浏览器上也能正常运行。
以下隐藏图像并用 link 替换它们(背景图像除外)。单击 link 将显示图像。
// ==UserScript==
// @name TCRF images
// @namespace SOMETHING
// @include http://YOUR_SERVER.COM/YOUR_PATH/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @version 1
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
GM_addStyle ( "\
* {\
background-image: none !important;\
}\
\
img.gmImgHideHidden {\
display: none !important;\
}\
" );
var num = 0;
function gmImgHideShowImg(imgId, linkId)
{
// Using plain JavaScript because the page itself may not have jquery
var img = document.getElementById(imgId);
img.className = img.className.replace( /(?:^|\s)gmImgHideHidden(?!\S)/g , '' );
var lnk = document.getElementById(linkId);
lnk.parentNode.removeChild(lnk);
}
// Exporting the "show image" function so that it can be used in the webpage
unsafeWindow.gmImgHideShowImg = exportFunction(gmImgHideShowImg, unsafeWindow);
waitForKeyElements ("img", hideImageExceptForAltText);
function hideImageExceptForAltText (jNode) {
var imgId = jNode.attr("id");
// Ensuring an id exists so the image can be searched for later
if(typeof(imgId) == "undefined")
{
imgId = "gmImgHideImg" + num;
jNode.attr("id", imgId);
}
var imgDisp = jNode.css("display");
var imgAlt = jNode.attr("alt");
jNode.addClass("gmImgHideHidden");
var linkId = "gmImgHideLink" + num;
var linkNode = $("<a></a>");
linkNode.attr("id", linkId);
linkNode.append("Image: " + imgAlt);
linkNode.attr("onclick", "gmImgHideShowImg('" + imgId + "', '" + linkId + "'); return false;");
jNode.parent().append(linkNode);
num++;
}
MutationObserver is without a doubt the best solution here. Combined with early injection by @run-at document-start
we can make the script pretty much bullet-proof. Check out this fiddle(使用 Firefox 40 测试)查看实际效果。
我认为代码是不言自明的。我已经注释了其中的细微之处,但如果您有任何不明白的地方,请发表评论。
// ==UserScript==
// @run-at document-start
// ==/UserScript==
"use strict";
/* part one: <img> elements */
(new MutationObserver(function(Records, Obs) {
for (let R of Records) {/* examine each mutation record: */
/* if the record specifies an attribute mutation… */
if (
R.attributeName === "src" &&
(R.target instanceof Element) && /* this check might be necessary */
R.target.tagName.toLowerCase() === "img" &&
R.target.getAttribute("src") !== "" /* avoid infinite loop */
) {
R.target.setAttribute("src", "");
};
/* if the record specifies a sub-element mutation… */
for (let N of R.addedNodes) {
if (
(N instanceof Element) && /* this check might be necessary */
N.tagName.toLowerCase() === "img" &&
N.getAttribute("src") !== "" /* avoid infinite loop */
) {
N.setAttribute("src", "");
};
};
};
})).observe(document, {
/* changes wot we listen for */
childList : true,
subtree : true,
attributes : true
});
/* part two: background-image styles */
let check_for_head_elem = function(_, Obs) {
if (!document.head) {return;};
Obs.disconnect();
/* apply our style */
let Style = document.createElement("style");
document.head.appendChild(Style);
Style.sheet.insertRule("* {background-image : none !important;}", 0);
};
let check_for_root_elem = function(_, Obs) {
if (!document.documentElement) {return;};
Obs.disconnect();
/* observe until the <head> element is added */
Obs = new MutationObserver(check_for_head_elem)
Obs.observe(document.documentElement, {childList : true});
check_for_head_elem(null, Obs); /* check here because it might exist already */
};
{/* observe until the <html> element is added */
let Obs = new MutationObserver(check_for_root_elem);
Obs.observe(document, {childList : true});
check_for_root_elem(null, Obs); /* check here because it might exist already */
};
还有一些我没有考虑到的在页面上获取图像的其他方法(<iframe>
、<svg>
、<canvas>
、<li>
要点),但如果有必要,您应该也可以使用突变观察器或 CSS 来处理这些问题。
一个简单、可靠的方法是先设置 CSS,然后再加载其他页面。
@run-at document-start
和 GM_addStyle()
这样做。 (在 Firefox 上;未在最新的 Tampermonkey 上测试)
这样一来,即使是几分之一秒也不会显示图像,就像使用原始代码或使用复杂、挑剔的 MutationObserver
方法一样。
这个完整的脚本显示了过程:
// ==UserScript==
// @name _Hide pics except for alt text
// @include http://YOUR_SERVER.COM/YOUR_PATH/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
GM_addStyle ( " \
* { \
background-image: none !important; \
} \
img { \
display: none !important; \
} \
" );
/*--- $(document).ready() is not always needed for modern Firefox, but
use for maximum portability, when script runs at document-start.
*/
$(document).ready ( function () {
waitForKeyElements ("img", hideImageExceptForAltText);
} );
function hideImageExceptForAltText (jNode) {
var imgAlt = jNode.attr("alt") || "";
var imgTitle = jNode.attr("title") || "";
jNode.after ('<span title="' + imgTitle + '">' + imgAlt + '</span>');
}
我试图确保不显示某个网站中的图片,但仍然显示替代文字。最初我试图用 Stylish(使用 Firefox)完成这个并问了以下问题:
接受的答案为我提供了使用 Greasemonkey 的替代解决方案。该脚本使用 waitForKeyElements 来隐藏图像,即使它们是使用 AJAX 添加的。
我将给定的脚本更改为以下内容:
// ==UserScript==
// @name _Hide pics except for alt text
// @include http://YOUR_SERVER.COM/YOUR_PATH/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// ==/UserScript==
GM_addStyle ( " \
* { \
background-image: none !important; \
} \
" );
waitForKeyElements ("img", hideImageExceptForAltText);
function hideImageExceptForAltText (jNode) {
var imgAlt = jNode.attr("alt");
var imgTitle = jNode.attr("title");
jNode.css("display", "none");
var newSpan = $("<span></span>");
newSpan.attr("title", imgTitle);
newSpan.append(imgAlt);
jNode.parent().append(newSpan);
}
就像原始脚本一样,这有一个问题,即在页面加载时图像仍然会显示片刻。
是否可以确保给定的函数将阻止页面上的图像立即显示,以便它们根本不可见?
编辑:Brock Adams 的回复提供了我遗漏的线索。如果有人正在寻找这样的东西,以下是我最终使用的。它在我需要它的网站上运行良好,但我不能保证它在除 Firefox 之外的其他网站或其他浏览器上也能正常运行。
以下隐藏图像并用 link 替换它们(背景图像除外)。单击 link 将显示图像。
// ==UserScript==
// @name TCRF images
// @namespace SOMETHING
// @include http://YOUR_SERVER.COM/YOUR_PATH/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @version 1
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
GM_addStyle ( "\
* {\
background-image: none !important;\
}\
\
img.gmImgHideHidden {\
display: none !important;\
}\
" );
var num = 0;
function gmImgHideShowImg(imgId, linkId)
{
// Using plain JavaScript because the page itself may not have jquery
var img = document.getElementById(imgId);
img.className = img.className.replace( /(?:^|\s)gmImgHideHidden(?!\S)/g , '' );
var lnk = document.getElementById(linkId);
lnk.parentNode.removeChild(lnk);
}
// Exporting the "show image" function so that it can be used in the webpage
unsafeWindow.gmImgHideShowImg = exportFunction(gmImgHideShowImg, unsafeWindow);
waitForKeyElements ("img", hideImageExceptForAltText);
function hideImageExceptForAltText (jNode) {
var imgId = jNode.attr("id");
// Ensuring an id exists so the image can be searched for later
if(typeof(imgId) == "undefined")
{
imgId = "gmImgHideImg" + num;
jNode.attr("id", imgId);
}
var imgDisp = jNode.css("display");
var imgAlt = jNode.attr("alt");
jNode.addClass("gmImgHideHidden");
var linkId = "gmImgHideLink" + num;
var linkNode = $("<a></a>");
linkNode.attr("id", linkId);
linkNode.append("Image: " + imgAlt);
linkNode.attr("onclick", "gmImgHideShowImg('" + imgId + "', '" + linkId + "'); return false;");
jNode.parent().append(linkNode);
num++;
}
MutationObserver is without a doubt the best solution here. Combined with early injection by @run-at document-start
we can make the script pretty much bullet-proof. Check out this fiddle(使用 Firefox 40 测试)查看实际效果。
我认为代码是不言自明的。我已经注释了其中的细微之处,但如果您有任何不明白的地方,请发表评论。
// ==UserScript==
// @run-at document-start
// ==/UserScript==
"use strict";
/* part one: <img> elements */
(new MutationObserver(function(Records, Obs) {
for (let R of Records) {/* examine each mutation record: */
/* if the record specifies an attribute mutation… */
if (
R.attributeName === "src" &&
(R.target instanceof Element) && /* this check might be necessary */
R.target.tagName.toLowerCase() === "img" &&
R.target.getAttribute("src") !== "" /* avoid infinite loop */
) {
R.target.setAttribute("src", "");
};
/* if the record specifies a sub-element mutation… */
for (let N of R.addedNodes) {
if (
(N instanceof Element) && /* this check might be necessary */
N.tagName.toLowerCase() === "img" &&
N.getAttribute("src") !== "" /* avoid infinite loop */
) {
N.setAttribute("src", "");
};
};
};
})).observe(document, {
/* changes wot we listen for */
childList : true,
subtree : true,
attributes : true
});
/* part two: background-image styles */
let check_for_head_elem = function(_, Obs) {
if (!document.head) {return;};
Obs.disconnect();
/* apply our style */
let Style = document.createElement("style");
document.head.appendChild(Style);
Style.sheet.insertRule("* {background-image : none !important;}", 0);
};
let check_for_root_elem = function(_, Obs) {
if (!document.documentElement) {return;};
Obs.disconnect();
/* observe until the <head> element is added */
Obs = new MutationObserver(check_for_head_elem)
Obs.observe(document.documentElement, {childList : true});
check_for_head_elem(null, Obs); /* check here because it might exist already */
};
{/* observe until the <html> element is added */
let Obs = new MutationObserver(check_for_root_elem);
Obs.observe(document, {childList : true});
check_for_root_elem(null, Obs); /* check here because it might exist already */
};
还有一些我没有考虑到的在页面上获取图像的其他方法(<iframe>
、<svg>
、<canvas>
、<li>
要点),但如果有必要,您应该也可以使用突变观察器或 CSS 来处理这些问题。
一个简单、可靠的方法是先设置 CSS,然后再加载其他页面。
@run-at document-start
和 GM_addStyle()
这样做。 (在 Firefox 上;未在最新的 Tampermonkey 上测试)
这样一来,即使是几分之一秒也不会显示图像,就像使用原始代码或使用复杂、挑剔的 MutationObserver
方法一样。
这个完整的脚本显示了过程:
// ==UserScript==
// @name _Hide pics except for alt text
// @include http://YOUR_SERVER.COM/YOUR_PATH/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
GM_addStyle ( " \
* { \
background-image: none !important; \
} \
img { \
display: none !important; \
} \
" );
/*--- $(document).ready() is not always needed for modern Firefox, but
use for maximum portability, when script runs at document-start.
*/
$(document).ready ( function () {
waitForKeyElements ("img", hideImageExceptForAltText);
} );
function hideImageExceptForAltText (jNode) {
var imgAlt = jNode.attr("alt") || "";
var imgTitle = jNode.attr("title") || "";
jNode.after ('<span title="' + imgTitle + '">' + imgAlt + '</span>');
}