LinkedIn Job Skills Highlighter

Highlight skills in LinkedIn job postings with color groups

// ==UserScript==
// @name         LinkedIn Job Skills Highlighter
// @namespace    http://github.com/ArmanJR
// @version      1.3
// @description  Highlight skills in LinkedIn job postings with color groups
// @author       Arman JR.
// @match        https://www.linkedin.com/jobs/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const skillsHighlight = {
        'strong': {
            color: '#0fe800',
            skills: ['go', 'golang', 'java']
        },
        'intermediate': {
            color: '#f5ed00',
            skills: ['spring']
        },
        'weak': {
            color: '#ff7f6b',
            skills: ['typescript', 'Node.js', 'JavaScript']
        }
    };

    let lastURL = location.href;

    // Wait for element to appear
    function waitFor(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const interval = 200;
            let elapsed = 0;

            const checkExist = setInterval(() => {
                const element = document.querySelector(selector);
                if (element) {
                    clearInterval(checkExist);
                    resolve(element);
                }
                elapsed += interval;
                if (elapsed >= timeout) {
                    clearInterval(checkExist);
                    reject(`Timeout: ${selector} not found`);
                }
            }, interval);
        });
    }

    // Click "Show more" if available
    async function clickShowMore() {
        console.log('Fired clickShowMore');
        try {
            const showMoreBtn = await waitFor('.feed-shared-inline-show-more-text__see-more-less-toggle', 1000);
            if (showMoreBtn.textContent.toLowerCase().includes('show more')) {
                console.log('Clicking "Show more" button');
                showMoreBtn.click();
            }
        } catch (e) {
            console.log('No "Show more" button found or timeout reached.');
        }
    }

    // Highlight skills in job description
    function highlightSkills() {
        console.log('Fired highlightSkills()');

        const contentDiv = document.querySelector('#job-details');
        if (!contentDiv) {
            console.log('Job description not found yet.');
            return;
        }

        //if (contentDiv.getAttribute('data-processed')) {
        //    console.log('Already processed, skipping.');
        //    return;
        //}

        const walker = document.createTreeWalker(contentDiv, NodeFilter.SHOW_TEXT, null, false);
        const textNodes = [];
        let node;

        while ((node = walker.nextNode())) {
            textNodes.push(node);
        }

        let highlightCount = 0;
        textNodes.forEach(textNode => {
            let text = textNode.textContent;
            let modified = false;

            for (const group of Object.values(skillsHighlight)) {
                group.skills.forEach(skill => {
                    const regex = new RegExp(`(^|\\W)(${skill.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})(?=\\W|$)`, 'gi');
                    if (regex.test(text)) {
                        modified = true;
                        highlightCount++;
                        text = text.replace(regex, `$1<span style="background-color:${group.color};padding:0 2px;border-radius:2px;font-size:1.1em;font-weight:500;">$2</span>`);
                    }
                });
            }

            if (modified) {
                const span = document.createElement('span');
                span.innerHTML = text;
                textNode.parentNode.replaceChild(span, textNode);
            }
        });

        contentDiv.setAttribute('data-processed', 'true');
        console.log(`Highlighted ${highlightCount} skills.`);
    }

    async function processJobDescription() {
        await clickShowMore();
        highlightSkills();
    }

    // Observe URL changes (LinkedIn dynamically loads content without page reloads)
    function observeUrlChanges() {
        new MutationObserver(() => {
            if (location.href !== lastURL) {
                lastURL = location.href;
                console.log('Detected URL change, processing new job...');
                setTimeout(processJobDescription, 100); // Slight delay to allow content to load
            }
        }).observe(document.body, { childList: true, subtree: true });
    }

    // Initial Run
    processJobDescription();
    observeUrlChanges();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址