【问题标题】:How to make my userscript work on specific pages of a site that uses the history api?如何使我的用户脚本在使用历史 API 的网站的特定页面上工作?
【发布时间】:2015-08-30 08:38:12
【问题描述】:

(这是this discussion的延续)

我一直在尝试制作一个脚本(用于 instagram),以显示在查看个人资料页面 (example profile) 时当前共有多少张图片可见。它在屏幕右上角显示计数器并支持无限滚动。

这里是:

// ==UserScript==
// @name        Instagram - visible images counter
// @name        Instagram - visible images counter
// @include     https://instagram.com/*
// @grant       none
// ==/UserScript==

var p = document.getElementsByClassName('-cx-PRIVATE-PostsGridItem__root');
var m = document.getElementsByClassName('-cx-PRIVATE-PostsStatistic__count');
var ww = m[0].innerHTML.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1");                       // Regex to strip the thousand comma seperator from the posts number
var z = (p.length/ww)*100;
var counter = p.length+' / '+m[0].innerHTML +' that is ' +z.toFixed(1) + '%';
var div = document.createElement("div");
div.style.top = "1px";
div.style.right = "1px";
div.style.position = "fixed";
document.body.appendChild(div);
div.id="mycounter";
mycounter = document.getElementById('mycounter');
mycounter.innerHTML = counter;
mycounter.style.top = "1px";
mycounter.style.right = "1px";
mycounter.style.position = "fixed";

/// ---------------------------------
/// mutation observer -monitors the Posts grid for infinite scrolling event- 
/// ---------------------------------
var target1 = document.querySelector('.-cx-PRIVATE-PostsGrid__root');
var observer1 = new MutationObserver(function (mutations) {
  mutations.forEach(function (mutation) {
    p=document.getElementsByClassName('-cx-PRIVATE-PostsGridItem__root');
    m = document.getElementsByClassName('-cx-PRIVATE-PostsStatistic__count');
    ww = m[0].innerHTML.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1");
    z = (p.length/ww)*100;
    counter = p.length+' / '+m[0].innerHTML +' that is ' +z.toFixed(1) + '%';    
    mycounter.innerHTML = counter;
  });
})
var config = { attributes: true, childList: true, characterData: true }
observer1.observe(target1, config);

我的脚本工作正常,但我遇到了这个问题:

Instagram,在最近重新设计后 - 我认为 - 似乎使用单页应用程序工作流程
即获取点击的链接内容并用它替换当前页面,然后更改浏览器的当前 URL,所有这些都没有实际重新加载页面

因此,我的脚本仅在我在新标签页中打开个人资料 URL 时才有效。
在打开个人资料 URL 时它不起作用在同一个标​​签中,而在我的时间线中 (https://instagram.com))。
换句话说,它不起作用(在我打开https://instagram.com 并登录后)
如果我点击查看我关注的用户的个人资料 URL,例如。 https://instagram.com/instagram/

我该如何解决这个问题?

Someone 提出了以下三种方式:

  1. 尝试使用event handler 处理 Instagram 网站触发的某些事件,例如 例如,github 上的pjax:end
  2. 使用 mutation observer 等待特定于配置文件的 html 元素。
  3. 或者使用setInterval定期检查location.href。*


所以,我一直在尝试各种方法(包括建议 #2 和 #3),但没有成功。
(关于建议 #1:我找不到任何类似于 pjax:end 的元素)。
所以,到目前为止我所尝试的:


  1. (基于建议 #2)添加另一个 mutation observer 以检查显示“帖子计数”元素的元素是否存在,如果存在,则运行我的代码。

    var target0 = document.querySelector('.-cx-PRIVATE-PostsStatistic__count');
    
    var observer0 = new MutationObserver(function (mutations) {
      mutations.forEach(function (mutation) {    
            myCode();
      });
    })
    
    var config0 = { attributes: true, childList: true, characterData: true }
    
    observer0.observe(target0, config0);
    

  1. (基于建议#3)每1秒检查一次location.href当前位置是否是配置文件(即不是时间线(https://instagram.com/)。如果为真则清除周期函数并运行我之前的代码。

    var interval = setInterval(testHref() , 1000);
    
    function testHref(){
        if (location.href != "https://instagram.com/")
            clearInterval(interval);
            myCode();   
    }
    

  1. 只需在我的代码顶部添加 1 秒延迟(并将 @require 规则更改为仅适用于配置文件 URL // @include https://instagram.com/*/),但没有成功:

    setTimeout(function() { }, 1000);
    

  1. 我什至尝试过使用 waitForKeyElements 实用程序函数,它可以检测和处理 AJAX 化内容。
    我认为以这种方式实现起来要容易得多,就像一个简单的“等到一个元素存在”一样工作(我只是使用主配置文件图片作为等待的选择器,因为我找不到任何相关的 AJAX 选择器。我也没有'不要在我的代码中使用 jNode)。
    所以我只是将我的整个代码包含在一个函数 visibleCounter 中,并添加了一个 waitForKeyElements 行(见下文),但不幸的是它也不起作用:

    waitForKeyElements (".-cx-PRIVATE-ProfilePage__avatar", visibleCounter);         
    
    function visibleCounter(jNode){
        myCode()
    }
    

【问题讨论】:

  • 如果选项 1 和 3 对您来说太高级,您应该花时间学习它们,以免它们看起来不高级。事件处理是 JS 脚本的基础。
  • 我总是尝试提高我对 javascript 的了解(无论是在线文档、书籍、stackexchange 网站等)。事件处理对我来说不再那么先进了——只是似乎没有任何效果,尽管我尝试过。

标签: javascript greasemonkey instagram html5-history


【解决方案1】:

我使用arrive.js 库解决了这个问题。它提供事件来监视 DOM 元素的创建和删除。它在内部使用了 Mutation Observers。该库不依赖 jQuery,您可以将下面示例中的 jQuery 元素替换为纯 javascript 元素,它可以正常工作。

我引用其作者comment

我一直觉得 MutationObserver api 有点复杂,所以我构建了一个 库,arrive.js,提供更简单的 api 来监听元素 创建/删除。

以及它的两个用途:

注意元素创建

使用到达事件来观察元素的创建:

// watch for creation of an element which satisfies the selector ".test-elem"
$(document).arrive(".test-elem", function() {
    // 'this' refers to the newly created element
    var $newElem = $(this);
});

注意删除元素

使用 leave 事件来观察元素是否被移除。第一个论据 leave 不能是后代或子选择器,即您不能通过 .page .test-elem 相反,通过 .test-elem。这是因为一个 MutationObserver 的 api 限制。

// watch for removal of an element which satisfies the selector ".test-elem"
$(".container-1").leave(".test-elem", function() {
    var $removedElem = $(this);
});  [1]: https://github.com/uzairfarooq/arrive

还有,这是complete script

// ==UserScript==
// @name        Instagram - visible images counter
// @include     https://www.instagram.com/*
// @grant       none
// @require     https://code.jquery.com/jquery-3.0.0.min.js
// @require     https://greasyfork.org/scripts/21927-arrive-js/code/arrivejs.js?version=139586
// ==/UserScript==




function showCounter() {
    var visibleCount = $( "a[href*='taken-by']" ).length;   // Count of visible images
    var totalString = $("span:contains('posts')").eq(1).children().eq(0).html();    // The 'total' value (it's a string)
    var total = totalString.replace(/(\d+),(?=\d{3}(\D|$))/g, '$1');    // Apply regex to 'total' string to strip the thousand comma seperator
    if (visibleCount > total){
        visibleCount = total;
    }
    var visiblePercent = ((visibleCount / total) * 100).toFixed(1); // Visible images as percentage
    var counter = visibleCount + ' / ' + totalString + ' that is ' + visiblePercent + '%';
    return counter;
}




function createDiv(){
    // Creation of the counter element
    document.body.appendChild(div);
    div.innerHTML = showCounter();      // Initial display of the counter
    div.style.top = '1px';
    div.style.right = '1px';
    div.style.position = 'fixed';
}



function observer(){

    /// ---------------------------------
    /// mutation observer -monitors the Posts grid for infinite scrolling event-.
    /// ---------------------------------
    observer1 = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            div.innerHTML = showCounter();                      // In each infinite scrolling event, re-calculate counter
        });
    }).observe($('article').children().eq(1).children()[0],     // target of the observer
        {
            // attributes: true,
            childList: true,
            // characterData: true,
        }); // config of the observer

}






var div = document.createElement('div');    // global variable
var observer1;                              // global variable

if (document.URL !== 'https://www.instagram.com/' &&
    document.URL.indexOf('https://www.instagram.com/p/') === -1 ){
    createDiv();
    observer();
}



$(document).arrive('article ._5axto', function() {      // 'article .5axto'
    createDiv();
    observer();
});



$(document).leave('article ._5axto', function() {
    div.remove();
    observer1.disconnect();
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多