Skip to content

Instantly share code, notes, and snippets.

@lwcorp
Created February 3, 2025 21:23
Show Gist options
  • Save lwcorp/b4d3ae4f3f7d79d8a6164f49cdcd3075 to your computer and use it in GitHub Desktop.
Save lwcorp/b4d3ae4f3f7d79d8a6164f49cdcd3075 to your computer and use it in GitHub Desktop.
LinkedIn Group sorting (both in search mode and when listing a user's groups) - including auto scrolling and optionally show just joined groups
(async function() {
// Helper: Converts text like "12K members" or "27,289 members" to a number.
// This version considers text until the first space or line break.
function parseMemberCount(text) {
text = text.replace(/,/g, '').trim();
const token = text.split(/[\s\n]/)[0];
const match = token.match(/([\d.]+)([KkMm]?)/);
if (!match) return 0;
let num = parseFloat(match[1]);
const suffix = match[2].toLowerCase();
if (suffix === 'k') {
num *= 1000;
} else if (suffix === 'm') {
num *= 1000000;
}
return num;
}
let container, countSelector, filterFn, items;
let isCase1 = false;
// Detect Case 1: items are direct <li> children of [role="list"]
const case1Item = document.querySelector('[role="list"]>li');
if (case1Item) {
container = case1Item.parentElement;
items = Array.from(container.querySelectorAll(':scope > li'));
// The member count is inside: div:nth-of-type(2) > div > div:nth-of-type(2)
countSelector = 'div:nth-of-type(2) > div > div:nth-of-type(2)';
// Filter: keep only items that do NOT contain a <button>.
filterFn = li => !li.querySelector('button');
isCase1 = true;
} else {
// Otherwise, attempt Case 2.
const case2Item = document.querySelector('.active li.pvs-list__paged-list-item');
if (!case2Item) {
alert("No groups found");
return;
}
container = case2Item.parentElement;
items = Array.from(container.querySelectorAll('li.pvs-list__paged-list-item'));
// The member count is in an element with class ".pvs-entity__caption-wrapper"
countSelector = '.pvs-entity__caption-wrapper';
// Filter: keep only items that have a <button> whose first trimmed line equals "Joined"
filterFn = li => {
const btn = li.querySelector('button');
if (!btn) return false;
const firstLine = btn.textContent.trim().split("\n")[0].trim();
return firstLine === "Joined";
};
}
// Function to detect if the page shows paging (e.g., pagination numbers).
function hasPaging() {
return !!document.querySelector('.artdeco-pagination__pages--number');
}
// Auto-scroll: scrolls down until no new content is loaded.
async function autoScrollToBottom() {
return new Promise(resolve => {
let lastHeight = document.documentElement.scrollHeight;
function scrollDown() {
window.scrollTo(0, document.documentElement.scrollHeight);
setTimeout(() => {
const newHeight = document.documentElement.scrollHeight;
if (newHeight > lastHeight) {
lastHeight = newHeight;
scrollDown();
} else {
resolve();
}
}, 1000);
}
scrollDown();
});
}
// If there's no paging element, auto-scroll down to trigger lazy loading,
// then scroll back up and wait a moment for the scroll up to complete.
if (!hasPaging()) {
await autoScrollToBottom();
window.scrollTo(0, 0);
await new Promise(resolve => setTimeout(resolve, 500)); // wait for the scroll-up to settle
// Re-select items in case additional groups were loaded.
if (isCase1) {
items = Array.from(container.querySelectorAll(':scope > li'));
} else {
items = Array.from(container.querySelectorAll('li.pvs-list__paged-list-item'));
}
}
// Sort the items by member count in descending order.
items.sort((a, b) => {
const aCountEl = a.querySelector(countSelector);
const bCountEl = b.querySelector(countSelector);
const aCount = aCountEl ? parseMemberCount(aCountEl.textContent) : 0;
const bCount = bCountEl ? parseMemberCount(bCountEl.textContent) : 0;
return bCount - aCount;
});
// Ask the user whether to show all groups or only the filtered ones.
const showAll = confirm("Show ALL groups? (OK = all; Cancel = filtered)");
const finalItems = showAll ? items : items.filter(filterFn);
// Clear the container and re-append the sorted (and optionally filtered) items.
container.innerHTML = "";
finalItems.forEach(item => container.appendChild(item));
})();
javascript:(async function(){function p(t){t=t.replace(/,/g,"").trim();const a=t.split(/[\s\n]/)[0],m=a.match(/([\d.]+)([KkMm]?)/);if(!m)return 0;let n=parseFloat(m[1]),s=m[2].toLowerCase();return s==="k"?n*=1e3:s==="m"&&(n*=1e6),n}let c,cs,f,its,isC1=false;const c1=document.querySelector('[role="list"]>li');if(c1){c=c1.parentElement;its=Array.from(c.querySelectorAll(":scope > li"));cs="div:nth-of-type(2) > div > div:nth-of-type(2)";f=li=>!li.querySelector("button");isC1=true}else{const c2=document.querySelector(".active li.pvs-list__paged-list-item");if(!c2){alert("No groups found");return;}c=c2.parentElement;its=Array.from(c.querySelectorAll("li.pvs-list__paged-list-item"));cs=".pvs-entity__caption-wrapper";f=li=>{const b=li.querySelector("button");if(!b)return!1;const fl=b.textContent.trim().split("\n")[0].trim();return fl==="Joined"}}function hasPaging(){return!!document.querySelector(".artdeco-pagination__pages--number")}async function sD(){return new Promise(r=>{let l=document.documentElement.scrollHeight;function d(){window.scrollTo(0,document.documentElement.scrollHeight);setTimeout(()=>{const nh=document.documentElement.scrollHeight;nh>l?(l=nh,d()):r()},1e3)}d()})}if(!hasPaging()){await sD();window.scrollTo(0,0);await new Promise(r=>setTimeout(r,500));its=isC1?Array.from(c.querySelectorAll(":scope > li")):Array.from(c.querySelectorAll("li.pvs-list__paged-list-item"))}its.sort((a,b)=>{const ae=a.querySelector(cs),be=b.querySelector(cs),ac=ae?p(ae.textContent):0,bc=be?p(be.textContent):0;return bc-ac});const sa=confirm("Show ALL groups? (OK = all; Cancel = filtered)"),fi=sa?its:its.filter(f);c.innerHTML="";fi.forEach(i=>c.appendChild(i))})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment