正确,javascript 无法检测是否在 Firefox 或 Chrome 中访问了链接 - 这是唯一适用于此 Greasemonkey 上下文的 2 个浏览器。
这是因为 Firefox 和 Chrome 非常重视安全和隐私。来自the CSS2 spec:
注意。样式表作者可能会滥用 :link 和 :visited 伪类来确定用户在未经用户同意的情况下访问了哪些站点。
因此,UA 可能会将所有链接视为未访问的链接,或实施其他措施来保护用户的隐私,同时以不同的方式呈现已访问和未访问的链接。有关处理隐私的更多信息,请参阅 [P3P]。
另请参阅,"Privacy and the :visited selector"
您可以看到一个演示,显示安全浏览器不会让您在jsfiddle.net/n8F9U 嗅探访问过的链接。
针对您的具体情况,由于您正在访问一个页面并使其保持打开状态,因此您可以帮助脚本跟踪访问过的链接。这不是万无一失的,但我相信它会满足您的要求。
首先,通过执行以下操作查看正在运行的脚本:
- 按原样安装脚本。
- 浏览到测试页面,jsbin.com/eledog。
每次重新加载或刷新时,测试页都会添加一个新链接。
-
GM 脚本在其运行的页面上添加了 2 个按钮。左上角的“开始/停止”按钮和右下角的“清除”按钮。
当您按下“开始”按钮时,它会执行以下操作:
- 页面上的所有现有链接都记录为“已访问”。
- 它启动一个计时器(默认设置:3 秒),当计时器关闭时,它会重新加载页面。
- 每次页面重新加载时,它都会打开所有新链接并启动新的重新加载计时器。
- 按“停止”按钮停止重新加载,访问的链接列表被保留。
“清除”按钮,清除访问过的页面列表。
警告:如果您在刷新循环处于活动状态时按“清除”,则下次重新加载页面时,所有链接将在新标签页中打开。
接下来,在您的网站上使用脚本...
仔细阅读脚本中的 cmets,您必须更改 @include、@exclude 和 selectorStr 值以匹配您正在使用的网站。
为获得最佳效果,禁用任何“重新加载每个”插件或“自动更新”选项。
重要提示:
-
脚本必须使用永久存储来跟踪链接。
选项包括:cookie、sessionStorage、localStorage、globalStorage、GM_setValue() 和 IndexedDB。
这些都有缺点,在这种情况下(单个站点,潜在的大量链接,多个会话),localStorage 是最好的选择(IndexedDB 可能是,但它仍然太不稳定 - 导致频繁FF 在我的机器上崩溃)。
这意味着只能在每个站点的基础上跟踪链接,并且“安全”、“隐私”或“更清洁”实用程序可以阻止或删除已访问链接的列表。 (就像,清除浏览器的历史记录会重置访问链接的所有 CSS 样式。)
目前该脚本仅适用于 Firefox。即使安装了 Tampermonkey,它也不应该在 Chrome 上运行,而无需进行一些重新设计。
脚本:
/*******************************************************************************
** This script:
** 1) Keeps track of which links have been clicked.
** 2) Refreshes the page at regular intervals to check for new links.
** 3) If new links are found, opens those links in a new tab.
**
** To Set Up:
** 1) Carefully choose and specify `selectorStr` based on the particulars
** of the target page(s).
** The selector string uses any valid jQuery syntax.
** 2) Set the @include, and/or, @exclude, and/or @match directives as
** appropriate for the target site.
** 3) Turn any "Auto update" features off. Likewise, do not use any
** "Reload Every" addons. This script will handle reloads/refreshes.
**
** To Use:
** The script will place 2 buttons on the page: A "Start/Stop" button in
** the upper left and a "Clear" button in the lower left.
**
** Press the "Start" button to start the script reloading the page and
** opening any new links.
** When the button is pressed, it is assumed that any existing links have
** been visited.
**
** Press the "Stop" button to halt the reloading and link opening.
**
** The "Clear" button erases the list of visited links -- which might
** otherwise be stored forever.
**
** Methodology:
** Uses localStorage to track state-machine state, and to keep a
** persistent list of visited links.
**
** Implemented with jQuery and some GM_ functions.
**
** For now, this script is Firefox-only. It probably will not work on
** Chrome, even with Tampermonkey.
*/
// ==UserScript==
// @name _New link / visited link, tracker and opener
// @include http://jsbin.com/*
// @exclude /\/edit\b/
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @grant GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
introduced in GM 1.0. It restores the sandbox.
*/
//--- Key control/setup variables:
var refreshDelay = 3000; //-- milliseconds.
var selectorStr = 'ul.topicList a.topicTitle';
//--- Add the control buttons.
$("body") .append ( '<div id="GM_StartStopBtn" class="GM_ControlWrap">'
+ '<button>Start checking for new links.</button></div>'
)
.append ( '<div id="GM_ClearVisitListBtn" class="GM_ControlWrap">'
+ '<button>Clear the list of visited links.</button></div>'
);
$('div.GM_ControlWrap').hover (
function () { $(this).stop (true, false).fadeTo ( 50, 1); },
function () { $(this).stop (true, false).fadeTo (900, 0.8); }// Coordinate with CSS.
);
//--- Initialize the link-handler object, but wait until the load event.
var stateMachine;
window.addEventListener ("load", function () {
stateMachine = new GM_LinkTrack ( selectorStr,
'#GM_StartStopBtn button',
'#GM_ClearVisitListBtn button',
refreshDelay
);
/*--- Display the current number of visited links.
We only update once per page load here.
*/
var numLinks = stateMachine.GetVisitedLinkCount ();
$("body").append ('<p>The page opened with ' + numLinks + ' visited links.</p>');
},
false
);
/*--- The link and state tracker object.
Public methods:
OpenAllNewLinks ()
StartStopBtnHandler ()
ClearVisitedLinkList ()
StartRefreshTimer ();
StopRefreshTimer ();
SetAllCurrentLinksToVisited ()
GetVisitedLinkCount ()
*/
function GM_LinkTrack (selectorStr, startBtnSel, clearBtnSel, refreshDelay)
{
var visitedLinkArry = [];
var numVisitedLinks = 0;
var refreshTimer = null;
var startTxt = 'Start checking for new links.';
var stopTxt = 'Stop checking links and reloading.';
//--- Get visited link-list from storage.
for (var J = localStorage.length - 1; J >= 0; --J) {
var itemName = localStorage.key (J);
if (/^Visited_\d+$/i.test (itemName) ) {
visitedLinkArry.push (localStorage[itemName] );
numVisitedLinks++;
}
}
function LinkIsNew (href) {
/*--- If the link is new, adds it to the list and returns true.
Otherwise returns false.
*/
if (visitedLinkArry.indexOf (href) == -1) {
visitedLinkArry.push (href);
var itemName = 'Visited_' + numVisitedLinks;
localStorage.setItem (itemName, href);
numVisitedLinks++;
return true;
}
return false;
}
//--- For each new link, open it in a separate tab.
this.OpenAllNewLinks = function ()
{
$(selectorStr).each ( function () {
if (LinkIsNew (this.href) ) {
GM_openInTab (this.href);
}
} );
};
this.StartRefreshTimer = function () {
if (typeof refreshTimer != "number") {
refreshTimer = setTimeout ( function() {
window.location.reload ();
},
refreshDelay
);
}
};
this.StopRefreshTimer = function () {
if (typeof refreshTimer == "number") {
clearTimeout (refreshTimer);
refreshTimer = null;
}
};
this.SetAllCurrentLinksToVisited = function () {
$(selectorStr).each ( function () {
LinkIsNew (this.href);
} );
};
this.GetVisitedLinkCount = function () {
return numVisitedLinks;
};
var context = this; //-- This seems clearer than using `.bind(this)`.
this.StartStopBtnHandler = function (zEvent) {
if (inRefreshCycle) {
//--- "Stop" pressed. Stop searching for new links.
$(startBtnSel).text (startTxt);
context.StopRefreshTimer ();
localStorage.setItem ('inRefreshCycle', '0'); //Set false.
}
else {
//--- "Start" pressed. Start searching for new links.
$(startBtnSel).text (stopTxt);
localStorage.setItem ('inRefreshCycle', '1'); //Set true.
context.SetAllCurrentLinksToVisited ();
context.StartRefreshTimer ();
}
inRefreshCycle ^= true; //-- Toggle value.
};
this.ClearVisitedLinkList = function (zEvent) {
numVisitedLinks = 0;
for (var J = localStorage.length - 1; J >= 0; --J) {
var itemName = localStorage.key (J);
if (/^Visited_\d+$/i.test (itemName) ) {
localStorage.removeItem (itemName);
}
}
};
//--- Activate the buttons.
$(startBtnSel).click (this.StartStopBtnHandler);
$(clearBtnSel).click (this.ClearVisitedLinkList);
//--- Determine state. Are we running the refresh cycle now?
var inRefreshCycle = parseInt (localStorage.inRefreshCycle, 10) || 0;
if (inRefreshCycle) {
$(startBtnSel).text (stopTxt); //-- Change the btn lable to "Stop".
this.OpenAllNewLinks ();
this.StartRefreshTimer ();
}
}
//--- Style the control buttons.
GM_addStyle ( " \
.GM_ControlWrap { \
opacity: 0.8; /*Coordinate with hover func. */ \
background: pink; \
position: fixed; \
padding: 0.6ex; \
z-index: 666666; \
} \
.GM_ControlWrap button { \
padding: 0.2ex 0.5ex; \
border-radius: 1em; \
box-shadow: 3px 3px 3px gray; \
cursor: pointer; \
} \
.GM_ControlWrap button:hover { \
color: red; \
} \
#GM_StartStopBtn { \
top: 0; \
left: 0; \
} \
#GM_ClearVisitListBtn { \
bottom: 0; \
right: 0; \
} \
" );