LinkedIn post highlighter

⚠️ Important disclaimer

The script probably conflicts with LinkedIn’s terms:

“Overlay or otherwise modify the Services or their appearance (such as by inserting elements into the Services or removing, covering, or obscuring an advertisement included on the Services)”.

If you want to strictly comply with LinkedIn’s terms, do NOT use this script. If you decide to use it, understand it’s probably against their terms even though it’s for personal use only.

Note for my future self: Review the legal stuff before writing a single line of code.

Even if I’m not going to use it, I’ll share the process and the code anyway as it might inspire somebody.

Why I created this

I got this idea from Tim Woodruff’s post:

But instead of just blocking (or tagging) things I don’t want to see, I though about highlighting things that I don’t want to miss either.

Highlighted LinkedIn post by Tim Woodruff. Displaying keyword "ServiceNow" with a green label.

How it works

The script (that you can find at the bottom of this post) runs in your browser using Tampermonkey.

  • Scans your LinkedIn timeline for keywords you define.
  • Highlights and labels posts containing terms you want to prioritise with coloured borders.
  • Labels posts with keywords you’d prefer to ignore.
  • Automatically processes new content as you scroll.
  • Refreshes periodically to catch updates.

I couldn’t find a solution using only CSS as I did for ServiceNow VTBs, so I went with JavaScript and DOM manipulation for this implementation.

How it looks

Some examples from my company’s timeline.

Single keyword match

When a post contains just one keyword:

  • The tag is displayed with its corresponding color.
  • The post gets a colored border for easy visual identification.
Highlighted LinkedIn post by SwissFlowIt. Displaying keyword "ServiceNow" with a green label.

Multiple keyword matches

When a post match multiple keywords:

  • All matching tags are displayed.
  • The color is based on the first matching keyword.
Highlighted LinkedIn post by SwissFlowIt. Displaying keywords "Documate", "Documentation" and "ServiceNow" with an orange label.

Excluding keywords

For posts containing terms you’ve flagged for exclusion:

  • No border is applied (doesn’t draw extra attention).
  • The keyword is displayed in a neutral gray label.
  • These take priority over highlight keywords.
Tagged LinkedIn post by SwissFlowIt. Displaying keyword "Tapping into" with a gray label indicating that it has been written by an AI.
Guilty of using AI-generated text to promote Documate’s AI capabilities!

Setting up the LinkedIn post highlighter

Installation

  1. Install Tampermonkey extension in your browser.
  2. Create a new script and paste the code below.

[Tested in Chrome Version 134.0.6998.44]

// ==UserScript==
// @name         LinkedIn post highlighter
// @match        https://www.linkedin.com/*
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const config = {
        highlightOpacity: '33', // 20% opacity in hex
        refreshInterval: 2000,
        initialDelay: 1000,
        scrollThrottle: 300
    };

    // Define CSS classes for LinkedIn elements
    // These classes are based on LinkedIn's current structure and may change over time
    // so they should be verified periodically
    const postClass = '.feed-shared-update-v2';
    const postContentClass = '.update-components-text';
    const postTag = 'span';

    // Define colors for highlight keywords
    // The order of the keywords in this object matters, as the first one found will be used
    // to determine the color of the post
    const keywordColors = {
        'Documate': '#f15a2a',
        'Raycast': '#0d6efd',
        'Documentation': '#e83e8c',
        'ServiceNow': '#81b5a1'
    };

    // Define keywords that will cause posts to be excluded
    const excludeKeywords = [
        'Delve',
        'Delving',
        'Dive into',
        'Exciting news',
        'Seamlessly',
        'Tapping into'
    ];

    // Exclude label color (gray)
    const excludeLabelColor = '#6c757d';

    // Add common styles as constants
    const COMMON_LABEL_STYLES = `
        color: white !important;
        padding: 8px 4px !important;
        position: absolute !important;
        top: 0 !important;
        left: -32px !important;
        display: flex !important;
        align-items: center !important;
        writing-mode: vertical-rl !important;
        text-orientation: mixed !important;
        transform: rotate(180deg) !important;
        font-weight: bold !important;
        border-radius: 0 8px 8px 0 !important;`;

    /**
     * Checks if a text contains a specific word as a whole word or hashtag
     *
     * @param {string} text - The text content to search within
     * @param {string} word - The word to search for
     * @returns {boolean} - True if the word is found as a whole word or hashtag, false otherwise
     */
    function containsWholeWord(text, word) {
        //Regex that handles various word boundaries and hashtags
        const regex = new RegExp(`(^|[^\\w])${word}($|[^\\w])|#${word}`, 'i');
        return regex.test(text);
    }

    /**
     * Creates and appends a label to a post with the given text and color
     *
     * @param {HTMLElement} post - The post element to attach the label to
     * @param {string} text - The text to display in the label
     * @param {string} color - The background color of the label (hex format)
     */
    function createAndAppendLabel(post, text, color) {
        const label = document.createElement('div');
        label.className = 'keyword-filter-label';
        label.textContent = text;
        label.setAttribute('style', `
            background: ${color} !important;
            ${COMMON_LABEL_STYLES}`);
        post.appendChild(label);
    }

    /**
     * Filters and highlights LinkedIn posts based on keywords
     *
     * This function scans all posts in the feed that haven't been processed yet,
     * checks for highlight keywords and exclude keywords, and applies appropriate
     * styling and labels based on the content.
     */
    function filterLinkedInPosts() {
        // Select all posts that are not already processed
        // and contain the specified classes
        const posts = document.querySelectorAll(`${postClass}:has(${postContentClass}):has(${postTag}):not(.processed-by-highlighter)`);
        const highlightWords = Object.keys(keywordColors);

        posts.forEach(post => {
            // Mark as processed
            post.classList.add('processed-by-highlighter');

            // Get ONLY the post's main content text (excluding comments and profiles)
            let postContent = post.querySelector(postContentClass);
            if (!postContent) return;

            // Extract only the text from the main post content
            const postText = postContent.textContent;

            // Check if post contains any highlighting keywords (whole word match)
            const containsHighlightKeyword = highlightWords.filter(keyword =>
                containsWholeWord(postText, keyword)
            );

            // Check if post contains any excluding keywords (whole word match)
            const containsExcludeKeyword = excludeKeywords.filter(keyword =>
                containsWholeWord(postText, keyword)
            );

            // Remove existing label if it exists
            const existingLabel = post.querySelector('.keyword-filter-label');
            if (existingLabel) {
                post.removeChild(existingLabel);
            }

            // First check for exclude keywords - they take priority
            if (containsExcludeKeyword.length > 0) {
                post.setAttribute('style', 'position: relative !important; border-radius: 8px !important;');
                createAndAppendLabel(post, containsExcludeKeyword.join(' / '), excludeLabelColor);
            }
            // Then check for highlight keywords
            else if (containsHighlightKeyword.length > 0) {
                // Get the first found keyword to determine the color
                const primaryKeyword = containsHighlightKeyword[0];
                const color = keywordColors[primaryKeyword];

                // Create a background with the same color but at 20% opacity
                post.setAttribute('style', `border: 2px solid ${color} !important; position: relative !important; opacity: 1 !important; border-radius: 8px !important;`);
                createAndAppendLabel(post, containsHighlightKeyword.join(' / '), color);
            }
        });
    }

    // Run once when page loads
    setTimeout(() => filterLinkedInPosts(), config.initialDelay);

    // Re-check when scrolling (LinkedIn loads content dynamically)
    window.addEventListener('scroll', () => {
        setTimeout(() => filterLinkedInPosts(), config.scrollThrottle);
    });

    // Also check periodically for new content
    setInterval(() => filterLinkedInPosts(), config.refreshInterval);
})();

Code language: JavaScript (javascript)

Customisation Tips

To personalise the script for your needs:

  • Change the keywords in the keywordColors object to terms relevant to your interests.
  • Add or remove terms from the excludeKeywords array to flag content you want to ignore.
  • Modify the colours to match your preferences.
  • Adjust the timing parameters in the config object if needed.

Ideas for improvement

  • Send the text post to an AI analyser and exclude posts based on a AI probability treshold instead of using “excluding keywords”.
  • Hide the posts instead of adding a tag (this definitely goes against LinkedIn’s terms).