Last active
May 9, 2025 07:58
-
-
Save wohfab/71e0785399afdf8f0b9eaeeaa9c58500 to your computer and use it in GitHub Desktop.
Collection of Tampermonkey scripts to improve the web.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name LinkedIn Post Permalinks | |
| // @namespace http://tampermonkey.net/ | |
| // @version 1.0 | |
| // @description Adds a clickable permalink to each LinkedIn post | |
| // @author wohfab | |
| // @license CC0 1.0 Universal – https://creativecommons.org/publicdomain/zero/1.0/ | |
| // @match https://www.linkedin.com/feed/* | |
| // @grant none | |
| // ==/UserScript== | |
| const LINKEDIN_PERMALINK_OPTIONS = { | |
| // CSS selector for LinkedIn post root element (must have activity urn) | |
| postRootSelector: 'div[data-id^="urn:li:activity:"]', | |
| // Regular expression to extract activity ID from the urn attribute | |
| activityUrnRegex: /urn:li:activity:(\d+)/, | |
| // CSS selector for the sub-description span where the link will be added | |
| subDescriptionSelector: '.update-components-actor__sub-description.text-body-xsmall.t-black--light', | |
| // CSS selector for the inner span inside the sub-description | |
| innerSpanSelector: 'span', | |
| // Class name for the injected permalink <a> element (used for duplicate detection) | |
| permalinkLinkClass: 'tm-permalink-link', | |
| // Text content for the permalink link | |
| permalinkLinkText: 'Permalink', | |
| // Base URL for LinkedIn activity permalinks (append activity ID) | |
| linkedinPermalinkBaseUrl: 'https://www.linkedin.com/feed/update/urn:li:activity/', | |
| // Target attribute for the permalink <a> element | |
| permalinkLinkTarget: '_blank', | |
| // How often (ms) to check for new posts (LinkedIn uses dynamic loading) | |
| domCheckIntervalMs: 1000 | |
| }; | |
| function addPermalinksToLinkedInPosts() { | |
| document.querySelectorAll(LINKEDIN_PERMALINK_OPTIONS.postRootSelector).forEach(postRootElement => { | |
| const activityUrn = postRootElement.getAttribute('data-id'); | |
| const activityIdMatch = activityUrn && activityUrn.match(LINKEDIN_PERMALINK_OPTIONS.activityUrnRegex); | |
| if (!activityIdMatch) return; | |
| const activityId = activityIdMatch[1]; | |
| const subDescriptionElement = postRootElement.querySelector(LINKEDIN_PERMALINK_OPTIONS.subDescriptionSelector); | |
| if (!subDescriptionElement) return; | |
| const innerSpanElement = subDescriptionElement.querySelector(LINKEDIN_PERMALINK_OPTIONS.innerSpanSelector); | |
| if (!innerSpanElement) return; | |
| // Avoid adding duplicate links | |
| if (innerSpanElement.querySelector('.' + LINKEDIN_PERMALINK_OPTIONS.permalinkLinkClass)) return; | |
| // Create the permalink <a> element | |
| const permalinkAnchor = document.createElement('a'); | |
| permalinkAnchor.href = LINKEDIN_PERMALINK_OPTIONS.linkedinPermalinkBaseUrl + activityId; | |
| permalinkAnchor.textContent = LINKEDIN_PERMALINK_OPTIONS.permalinkLinkText; | |
| permalinkAnchor.className = LINKEDIN_PERMALINK_OPTIONS.permalinkLinkClass; | |
| permalinkAnchor.target = LINKEDIN_PERMALINK_OPTIONS.permalinkLinkTarget; | |
| innerSpanElement.appendChild(document.createTextNode(' ')); | |
| innerSpanElement.appendChild(permalinkAnchor); | |
| }); | |
| } | |
| addPermalinksToLinkedInPosts(); | |
| setInterval(addPermalinksToLinkedInPosts, LINKEDIN_PERMALINK_OPTIONS.domCheckIntervalMs); | |
| const domMutationObserver = new MutationObserver(addPermalinksToLinkedInPosts); | |
| domMutationObserver.observe(document.body, { childList: true, subtree: true }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment