(This is in continuation of this discussion)
I've been trying to make a script (for instagram) that shows how many images out of total are currently visible when viewing a profile page (example profile). It shows the counter in the upper right corner of the screen and supports infinite scrolling.
Here it is:
// ==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);
My script works ok, but I have this issue:
Instagram, after it's recent redesign -I think-, seems to use single-page application workflow,
i.e fetches the clicked link content and replaces the current page with it, and then changes the browser's current URL, all without actually reloading the page.
So, my script only works when I open a profile URL in a new tab.
It doesn't work when opening a profile URL in the same tab while in my timeline (https://instagram.com)).
In other words, it doesn't work (after I open https://instagram.com and login)
if I click to view a profile URL of a user I follow, eg. https://instagram.com/instagram/
How can I fix this?
Someone has kindly suggested these 3 ways:
- 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.*
So, I've been trying various approaches (including the suggestions #2 and #3) but no success.
(about suggestion #1: I can't find any element similar to pjax:end
).
So, what I've tried so far:
(based on suggestion #2) adding another
mutation observer
to check whether the element that shows the 'posts count' element exists, and if yes, then run my code.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);
(based on suggestion #3) checking
location.href
every 1 second whether the current location is a profile (i.e. not the timeline (https://instagram.com/). If true then clear the periodic function and run my previous code.var interval = setInterval(testHref() , 1000); function testHref(){ if (location.href != "https://instagram.com/") clearInterval(interval); myCode(); }
simply adding a 1 sec delay on top of my code (and changing the @require rule to apply only on profile URLs
// @include https://instagram.com/*/
), but no success:setTimeout(function() { }, 1000);
I've even tried using the waitForKeyElements utility function, which detects and handles AJAXed content.
I thought it's much easier to implement this way, working as a simple "wait until an element exists" (I just used the main profile pic as the selector to wait for, because I couldn't find any relevant AJAX selector. Also I didn't use jNode inside my code).
So I just enclosed my whole code in a single functionvisibleCounter
, and added a waitForKeyElements line (see below), but unfortunately it also doesn't work:waitForKeyElements (".-cx-PRIVATE-ProfilePage__avatar", visibleCounter); function visibleCounter(jNode){ myCode() }
I solved this using the arrive.js library. It provides events to watch for DOM elements creation and removal. It makes use of Mutation Observers internally. The library does not depend on jQuery, you can replace jQuery elements in the examples below with pure javascript elements and it would work fine.
I quote this comment by its author:
as well as just two of its uses:
and
And, this is the complete script: