如何检测页面上已访问和未访问的链接?

IT技术 javascript firefox hyperlink click greasemonkey
2021-03-10 02:04:54

我的目标是检测网页上未访问的链接,然后创建一个greasemonkey 脚本来点击这些链接。此处未访问的链接是指我未打开的链接。由于我可以看到所有浏览器都提供了更改已访问和未访问链接颜色的功能,因此有可能以任何方式检测这些链接。在搜索时我发现了这个链接:http : //www.mozdev.org/pipermail/greasemonkey/2005-November/006821.html但这里有人告诉我这不再可能。请帮忙。

2个回答

正确,javascript 无法检测是否在 Firefox 或 Chrome 中访问了链接——这是在此Greasemonkey上下文中唯一适用的 2 种浏览器

这是因为 Firefox 和 Chrome 非常重视安全和隐私。来自CSS2 规范

笔记。样式表作者可能会滥用 :link 和 :visited 伪类来确定用户在未经用户同意的情况下访问了哪些站点。

因此,UA 可能会将所有链接视为未访问链接,或实施其他措施以保护用户隐私,同时以不同方式呈现访问过和未访问过的链接。有关处理隐私的更多信息,请参阅 [P3P]。

另请参阅“隐私和 :visited 选择器”
您可以在jsfiddle.net/n8F9U 上看到一个演示,该演示表明安全浏览器不会让您嗅探访问过的链接




对于您的特定情况,因为您正在访问一个页面并保持打开状态,您可以帮助脚本跟踪访问了哪些链接。这不是万无一失的,但我相信它会满足您的要求。

首先,通过执行以下操作查看正在运行的脚本

  1. 按原样安装脚本。
  2. 浏览到测试页面jsbin.com/eledog
    每次重新加载或刷新时,测试页面都会添加一个新链接。
  3. GM 脚本向其运行的页面添加了 2 个按钮。左上角的“开始/停止”按钮和右下角的“清除”按钮。

    当您按下“开始”按钮时,它会执行以下操作:

    1. 页面上的所有现有链接都记录为“已访问”。
    2. 它启动一个计时器(默认设置:3 秒),当计时器关闭时,它会重新加载页面。
    3. 每次页面重新加载时,它都会打开任何新链接并启动新的重新加载计时器。
    4. 按“停止”按钮停止重新加载,访问的链接列表被保留。

    “清除”按钮清除访问过的页面列表。
    警告:如果在刷新循环处于活动状态时按“清除”,则下次页面重新加载时,所有链接都将在新选项卡中打开。


接下来,要在您的网站上使用脚本...

仔细阅读剧本的意见,你将不得不改变@include@excludeselectorStr值,您正在使用的网站相匹配。

为获得最佳效果,请禁用任何“重新加载每个”附加组件或“自动更新”选项。


重要笔记:

  1. 该脚本必须使用永久存储来跟踪链接。
    选项有:饼干,sessionStoragelocalStorageglobalStorageGM_setValue(),和IndexedDB

    这些都有缺点,在这种情况下(单个站点,可能有大量链接,多个会话),localStorage是最好的选择(IndexedDB可能是,但它仍然太不稳定——导致我的机器上频繁出现 FF 崩溃)。

    这意味着只能在每个站点的基础上跟踪链接,“安全”、“隐私”或“更清洁”实用程序可以阻止或删除访问过的链接列表。(就像,清除浏览器的历史记录将重置访问过的链接的任何 CSS 样式。)

  2. 该脚本目前仅适用于 Firefox。它不应该在 Chrome 上工作,即使安装了 Tampermonkey,没有一点重新设计。



剧本:

/*******************************************************************************
**  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;                                              \
    }                                                                       \
" );
@Chetan,第二篇文章只是造型技巧,仍然不会帮助你。但是,鉴于您的使用模式,有一种可能的解决方法。我会解决这些问题并在大约一天内发布,如果没有人打败我的话。
2021-04-17 02:04:54
您忽略了 JavaScript 可以在各种安全上下文中运行的事实。Greasemonkey 是一个 Firefox 插件,因此当然可以访问链接的访问状态。但是,我认为这不会暴露给用户脚本。
2021-05-08 02:04:54
另外,在我之前的评论中,我只想知道未访问的链接,而不是访问过的链接,修复后是否有可能?我还发现了另一个网页,作者在安全修复后正在做这样的事情:webdesignfromscratch.com/html-css/...
2021-05-09 02:04:54
感谢您在我的页面中回复良好,有一个自动更新选项我不知道它是如何工作的,但我已将其关闭(自动更新选项)并通过使用 firefox 插件“Reload Every”不断刷新页面以检查新链接. 如果这能回答您的问题,请告诉我。
2021-05-12 02:04:54
@Pumbaa80:附加组件可以检测访问过的链接可能是真的,但它们也不限于 JS。无论如何,Greasemonkey 不会向 GM脚本公开任何增强的样式或访问状态信息
2021-05-14 02:04:54

您可以解析页面上的所有链接并获取它们的 CSS 颜色属性。如果链接的颜色与您在 CSS 中定义的未访问链接的颜色匹配,则此链接未访问。

这种技术通常用于确定所有访问过的链接。这是一种安全漏洞,可让您确定用户是否访问了特定网站。通常由低俗的营销人员使用。

这种伎俩通常被归类为“浏览器的历史操纵技巧”。

更多代码信息:http : //www.stevenyork.com/tutorial/getting_browser_history_using_javascript

jsfiddle 正确演示了 Firefox 中的安全性,颜色对 javascript 隐藏。
2021-04-22 02:04:54
链接现已损坏。
2021-05-03 02:04:54
不。浏览器多年前就关闭了这个安全漏洞。jsfiddle.net/n8F9U 上亲自查看
2021-05-05 02:04:54
您可能想/需要知道某个链接是否被点击,有很多值得尊敬的理由。
2021-05-13 02:04:54