如何使我的用户脚本在使用历史记录 api 的站点的特定页面上工作?
How to make my userscript work on specific pages of a site that uses the history api?
(这是 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, ""); // 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, "");
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,在最近重新设计之后 - 我认为 - 似乎使用 单页应用程序工作流程,
即获取点击的 link 内容并用它替换当前页面,然后更改浏览器的当前 URL, 而无需实际重新加载页面.
因此,我的脚本仅在我打开配置文件时有效 URL 在新选项卡中。
打开配置文件时不起作用URL在同一选项卡中而在我的时间轴中(https://instagram.com)) .
换句话说,它不起作用(在我打开 https://instagram.com 并登录后)
如果我点击查看我关注的用户的个人资料 URL,例如。 https://instagram.com/instagram/
我该如何解决?
Someone 亲切地建议了这 3 种方式:
- Try an
event handler
for some event fired by the Instagram site like
pjax:end on github, for example.
- Use a
mutation observer
that waits a profile-specific html element.
- Or use
setInterval
to check location.href periodically.*
所以,我一直在尝试各种方法(包括建议 #2 和 #3)但没有成功。
(关于建议 #1:我找不到任何类似于 pjax:end
的元素)。
所以,到目前为止我已经尝试过:
(根据建议#2)添加另一个mutation observer
检查显示'posts count'元素的元素是否存在,如果存在,则运行我的代码。
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);
(基于建议 #3)每 1 秒检查一次 location.href
当前位置是否是配置文件(即不是时间线(https://instagram.com/)。如果为真,则清除周期函数和 运行 我以前的代码。
var interval = setInterval(testHref() , 1000);
function testHref(){
if (location.href != "https://instagram.com/")
clearInterval(interval);
myCode();
}
只是在我的代码之上添加了 1 秒的延迟(并将 @require 规则更改为仅应用于配置文件 URLs // @include https://instagram.com/*/
),但没有成功:
setTimeout(function() { }, 1000);
我什至尝试过使用 waitForKeyElements 实用程序功能,它可以检测和处理 AJAXed 内容。
我认为以这种方式实现起来要容易得多,作为一个简单的 "wait until an element exists" 工作(我只是使用主要个人资料图片作为等待的选择器,因为我找不到任何相关的 AJAX 选择器。也我没有在我的代码中使用 jNode。
所以我只是将我的整个代码包含在一个函数 visibleCounter
中,并添加了一个 waitForKeyElements 行(见下文),但不幸的是它也不起作用:
waitForKeyElements (".-cx-PRIVATE-ProfilePage__avatar", visibleCounter);
function visibleCounter(jNode){
myCode()
}
我使用 arrive.js 库解决了这个问题。它提供事件来监视 DOM 元素的创建和删除。它在内部使用 Mutation Observers。该库不依赖于 jQuery,您可以将下面示例中的 jQuery 元素替换为纯 javascript 元素,它会正常工作。
我引用它的作者comment:
I've always found MutationObserver api a bit complex so I've built a
library, arrive.js, to provide a simpler api to listen for elements
creation/removal.
以及它的两个用途:
Watch for elements creation:
Use arrive event to watch for elements creation:
// 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);
});
和
Watch for elements removal
Use leave event to watch for elements removal. The first arugument to
leave must not be a descendent or child selector i.e. you cannot pass
.page .test-elem, instead, pass .test-elem. It's because of a
limitation in MutationObserver's 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, ''); // 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();
});
(这是 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, ""); // 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, "");
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,在最近重新设计之后 - 我认为 - 似乎使用 单页应用程序工作流程,
即获取点击的 link 内容并用它替换当前页面,然后更改浏览器的当前 URL, 而无需实际重新加载页面.
因此,我的脚本仅在我打开配置文件时有效 URL 在新选项卡中。
打开配置文件时不起作用URL在同一选项卡中而在我的时间轴中(https://instagram.com)) .
换句话说,它不起作用(在我打开 https://instagram.com 并登录后)
如果我点击查看我关注的用户的个人资料 URL,例如。 https://instagram.com/instagram/
我该如何解决?
Someone 亲切地建议了这 3 种方式:
- Try an
event handler
for some event fired by the Instagram site like pjax:end on github, for example.- Use a
mutation observer
that waits a profile-specific html element.- Or use
setInterval
to check location.href periodically.*
所以,我一直在尝试各种方法(包括建议 #2 和 #3)但没有成功。
(关于建议 #1:我找不到任何类似于 pjax:end
的元素)。
所以,到目前为止我已经尝试过:
(根据建议#2)添加另一个
mutation observer
检查显示'posts count'元素的元素是否存在,如果存在,则运行我的代码。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);
(基于建议 #3)每 1 秒检查一次
location.href
当前位置是否是配置文件(即不是时间线(https://instagram.com/)。如果为真,则清除周期函数和 运行 我以前的代码。var interval = setInterval(testHref() , 1000); function testHref(){ if (location.href != "https://instagram.com/") clearInterval(interval); myCode(); }
只是在我的代码之上添加了 1 秒的延迟(并将 @require 规则更改为仅应用于配置文件 URLs
// @include https://instagram.com/*/
),但没有成功:setTimeout(function() { }, 1000);
我什至尝试过使用 waitForKeyElements 实用程序功能,它可以检测和处理 AJAXed 内容。
我认为以这种方式实现起来要容易得多,作为一个简单的 "wait until an element exists" 工作(我只是使用主要个人资料图片作为等待的选择器,因为我找不到任何相关的 AJAX 选择器。也我没有在我的代码中使用 jNode。
所以我只是将我的整个代码包含在一个函数visibleCounter
中,并添加了一个 waitForKeyElements 行(见下文),但不幸的是它也不起作用:waitForKeyElements (".-cx-PRIVATE-ProfilePage__avatar", visibleCounter); function visibleCounter(jNode){ myCode() }
我使用 arrive.js 库解决了这个问题。它提供事件来监视 DOM 元素的创建和删除。它在内部使用 Mutation Observers。该库不依赖于 jQuery,您可以将下面示例中的 jQuery 元素替换为纯 javascript 元素,它会正常工作。
我引用它的作者comment:
I've always found MutationObserver api a bit complex so I've built a library, arrive.js, to provide a simpler api to listen for elements creation/removal.
以及它的两个用途:
Watch for elements creation:
Use arrive event to watch for elements creation:
// 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); });
和
Watch for elements removal
Use leave event to watch for elements removal. The first arugument to leave must not be a descendent or child selector i.e. you cannot pass .page .test-elem, instead, pass .test-elem. It's because of a limitation in MutationObserver's 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, ''); // 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();
});