Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save wohfab/71e0785399afdf8f0b9eaeeaa9c58500 to your computer and use it in GitHub Desktop.

Select an option

Save wohfab/71e0785399afdf8f0b9eaeeaa9c58500 to your computer and use it in GitHub Desktop.
Collection of Tampermonkey scripts to improve the web.
// ==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