NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Instagram - visible images counter // @namespace darkred // @version 2024.2.23 // @description Shows in instagram profile pages how many images out of total (as a number and as a percentage) are currently visible, as you scroll down the page. // @author darkred // @license MIT // @include https://www.instagram.com/* // @grant none // @require https://code.jquery.com/jquery-3.2.1.min.js // @require https://greasyfork.org/scripts/21927-arrive-js/code/arrivejs.js // @supportURL https://github.com/darkred/Userscripts/issues // ==/UserScript== /* eslint-disable no-console */ const escapeHTMLPolicy = (({ trustedTypes }, policy) => trustedTypes ? trustedTypes.createPolicy('myEscapePolicy', policy) : policy)(window, { createHTML: (str) => str, }); var stylesheet = `<style> .counter { color: #D9D9D9 !important; } </style>`; $('head').append(stylesheet); // Not needed anymore /* // If you scroll down, beyond the first 12 images, then the "LOAD MORE" button(to show more images) will be automatically clicked $(window).scroll(function() { if ($(window).scrollTop() + $(window).height() > $(document).height() - 100) { var element = $(`a:contains('Load more')`)[0]; element.click(); } }); */ var hrefselems = []; var hrefs = []; var total; function showCounter() { // var totalString = $(`span:contains('posts'):last-child > span, .g47SY`).html(); // The 'total' value (it's a string). The ".g47SY" selector is for localized pages, e.g. https://www.instagram.com/instagram/?hl=de // var totalString = document.querySelector(`#react-root > section > main > div > header > section > ul > li:nth-child(1) > * > span`).textContent; // The 'total' value (it's a string). The ".g47SY" selector is for localized pages, e.g. https://www.instagram.com/instagram/?hl=de var totalString = document.querySelector(`[id^=mount_0_0_] section > main > div > header > section > ul > li:nth-child(1) > * > span`).textContent; // The 'total' value (it's a string). The ".g47SY" selector is for localized pages, e.g. https://www.instagram.com/instagram/?hl=de total = totalString.replace(',', '').replace('.', ''); // strip the thousand comma/dot seperator // hrefselems = document.querySelectorAll(`a[href*='taken-by']`); // hrefselems = document.querySelectorAll(`._aabd._aa8k._aanf > a`); // hrefselems = document.querySelectorAll(`._aabd._aa8k._al3l > a`); hrefselems = document.querySelectorAll(`._aabd._aa8k.x2pgyrj > a`); $.each(hrefselems, function(index, value) { // hrefs.indexOf(String(value)) === -1 ? hrefs.push(String(value)) : console.log("This item already exists"); // https://stackoverflow.com/a/36683363 if (hrefs.indexOf(String(value)) === -1) { // hrefs.count -below- serves as a counter for the newly added displayed images (on each infinite scrolling event) hrefs.push(String(value)); } }); var visibleCount = hrefs.length; var visiblePercent = ((visibleCount / total) * 100).toFixed(1); // Visible images count as percentage if (isNaN(visiblePercent)) { visiblePercent = 0 ; // avoid NaN } 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.innerHTML = escapeHTMLPolicy.createHTML(showCounter()); // Initial display of the counter div.style.top = '1px'; div.style.right = '1px'; div.style.position = 'fixed'; div.className = 'counter'; } function createObserver() { // var thePics = document.querySelector('div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]'); var thePics = document.querySelector('div[style*="flex-direction: column;"], div[style$="padding-top: 0px;"]'); // the "pics" area element, with rows that contain 3 pics each (watching for 'row' element additions) --> https://stackoverflow.com/a/5110337 ("wildcard * in CSS") starting (^=) with x and ending ($=) with y if (!thePics){ return; } hrefselems.length = 0; // empty the array (see https://stackoverflow.com/a/1232046, method #2) hrefs.length = 0; /// --------------------------------- /// mutation observer -monitors the Posts grid for infinite scrolling event-. /// --------------------------------- observer = new MutationObserver(function() { // --> Callback function to execute when mutations are observed if (div.innerHTML.indexOf(total + ' / ' + total) === -1) { // div.innerHTML = showCounter(); // On each infinite scrolling event, re-calculate counter div.innerHTML = escapeHTMLPolicy.createHTML(showCounter()); // On each infinite scrolling event, re-calculate counter } // }).observe(document.querySelector('._havey'), // target of the observer: the "pics" area element, with rows that contain 3 pics each (watching for 'row' element additions) // }).observe(document.querySelector('div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]'), // target of the observer: the "pics" area element, with rows that contain 3 pics each (watching for 'row' element additions) }); observer.observe(thePics, // target of the observer { // attributes: true, childList: true, // characterData: true, // subtree: true, }); // config of the observer } var div = document.createElement('div'); var observer; // var avatarSelector = 'span[style="width: 152px; height: 152px;"]'; // the profile's photo/avatar element // var avatarSelector = 'main > article > header > section > div._ienqf > div > button'; // the 3-dots icon // var avatarSelector = 'div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]'; // the 3-dots icon // var avatarSelector = '._mainc'; // the profile's bio area element // var avatarSelector = 'h1.notranslate'; // the profile name element // var avatarSelector = 'h1.rhpdm'; // the profile name element // var avatarSelector = 'span.-nal3'; // the 'posts' count element, e.g. 683 posts // var avatarSelector = 'ul.k9GMp'; // the profile's 3 counters container element // var avatarSelector = '.eC4Dz'; // the profile's username container element // var avatarSelector = '._aa_m'; // the profile's username container element // var avatarSelector = '._aa_c'; // the profile's username container element // var avatarSelector = 'main > article > header > section > div._ienqf > div > button'; // the 3-dots icon // var avatarSelector = 'div[style="flex-direction: column; padding-bottom: 0px; padding-top: 0px;"]'; // the 3-dots icon var avatarSelector = '.x1q0g3np.x2lah0s.x8j4wrb'; // the 3-dots icon if (document.querySelector(avatarSelector) ) { if (!document.querySelector('.counter')){ createDiv(); createObserver(); } } else { console.log('ERROR: Cannot create the Counter element, the avatarSelector element ( ' + avatarSelector + ' ) doesn\'t exist !!'); } document.arrive(avatarSelector, function() { // the avatar in the profile page createDiv(); createObserver(); // alert() }); function removeCounter(){ div.remove(); hrefselems.length = 0; // empty the array (see https://stackoverflow.com/a/1232046, method #2) hrefs.length = 0; total = ''; observer.disconnect(); // if (observer) { // observer.disconnect(); // } } document.leave(avatarSelector, function() { if (!document.querySelector(avatarSelector)){ removeCounter(); } }); // when navigating using the browser's back/forth window.addEventListener('popstate', function () { // alert() removeCounter(); console.log('COUNTER IS REMOVED'); // TODO // createDiv(); // createObserver(); }); // when navigating from a profile to another via searchbox // history API) // https://stackoverflow.com/questions/56760727/how-to-observe-a-change-in-the-url-using-javascript (function(){ var rs = history.pushState; history.pushState = function(state, title, url){ if ( !url.includes('/following/') && !url.includes('/followers/') && !url.includes('/p/') && !url.includes('/direct/') && !window.location.href.includes('/p/') ){ // avoid all possible in-page/popup "pages" (a profile's following/followers, and posts -opened by clicking on a thumb-) rs.apply(history, arguments); // preserve normal functionality console.log('navigating', arguments); // do something extra here; raise an event removeCounter(); // alert() } }; }());