Skip to content

Instantly share code, notes, and snippets.

@JayCuthrell
Last active August 2, 2026 00:18
Show Gist options
  • Select an option

  • Save JayCuthrell/de951960b96b94d2afe11918b3ff02b6 to your computer and use it in GitHub Desktop.

Select an option

Save JayCuthrell/de951960b96b94d2afe11918b3ff02b6 to your computer and use it in GitHub Desktop.
LinkedIn Feed cleaner to unfollow everything that can be unfollowed
javascript:(async () => {
const MAX_UNFOLLOWS = 25;
const MIN_DELAY_MS = 4000;
const MAX_DELAY_MS = 8000;
const DROPDOWN_WAIT_MS = 1000;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const getRandomDelay = () => Math.floor(Math.random() * (MAX_DELAY_MS - MIN_DELAY_MS + 1)) + MIN_DELAY_MS;
const simulateFullClick = (el) => {
['pointerdown', 'mousedown', 'mouseup', 'click'].forEach((type) => {
el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window }));
});
};
const getUnprocessedPosts = () => {
return Array.from(document.querySelectorAll('[role="listitem"], .feed-shared-update-v2, div[data-id*="urn:li:activity"]'))
.filter((post) => post.dataset.scriptProcessed !== "true");
};
const getPostAuthor = (post, menuBtn) => {
if (menuBtn) {
const aria = menuBtn.getAttribute('aria-label') || '';
const match = aria.match(/control menu for post by (.+)/i);
if (match && match[1]) return match[1].trim();
}
const authorEl = post.querySelector('strong, span[dir="ltr"], a[href*="/in/"], a[href*="/company/"]');
return authorEl ? authorEl.innerText.split('\n')[0].trim() : 'Unknown Creator/Page';
};
console.log("πŸš€ Starting Feed Unfollow Bookmarklet...");
let processedCount = 0;
while (processedCount < MAX_UNFOLLOWS) {
const posts = getUnprocessedPosts();
if (posts.length === 0) {
console.log("πŸ“œ Scrolling feed...");
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
await sleep(4000);
if (getUnprocessedPosts().length === 0) break;
continue;
}
const currentPost = posts[0];
currentPost.dataset.scriptProcessed = "true";
const menuBtn = currentPost.querySelector('button[aria-label*="control menu" i], button[aria-label*="Control menu" i], button.feed-shared-control-menu__trigger');
if (!menuBtn) continue;
const author = getPostAuthor(currentPost, menuBtn);
currentPost.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(800);
simulateFullClick(menuBtn);
await sleep(DROPDOWN_WAIT_MS);
const menuItems = Array.from(document.querySelectorAll('[role="menuitem"], .feed-shared-control-menu__content button, div[role="menu"] button'));
const unfollowItem = menuItems.find((item) => {
const text = (item.innerText || item.textContent || '').trim().toLowerCase();
return text.includes('unfollow') || text.includes('stop following');
});
if (unfollowItem && document.body.contains(unfollowItem)) {
const itemText = unfollowItem.innerText.trim();
simulateFullClick(unfollowItem);
processedCount++;
console.log(`[${processedCount}/${MAX_UNFOLLOWS}] βœ… ${itemText || `Unfollowed ${author}`}`);
const delay = getRandomDelay();
console.log(` -> Waiting ${(delay / 1000).toFixed(1)}s...`);
await sleep(delay);
} else {
simulateFullClick(menuBtn);
console.log(`⏩ [Skip] Unfollow unavailable for: ${author}`);
await sleep(1000);
}
}
alert(`πŸŽ‰ Batch finished! Verified unfollows: ${processedCount}`);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment