Skip to content

Instantly share code, notes, and snippets.

@AurelienGasser
Created February 23, 2023 10:04
Show Gist options
  • Save AurelienGasser/aec7603cf144aa0b01881c9ef9035d04 to your computer and use it in GitHub Desktop.
Save AurelienGasser/aec7603cf144aa0b01881c9ef9035d04 to your computer and use it in GitHub Desktop.
Auto-expand list of experience items, color items based on duration, strikethrough based on keyboard, hide previously seen candidates.
// LinkedIn recruiter search results highlighter.
// Paste into your browser's JavaScript console.
//
// Features:
// - Auto-expand experience items
// - Color experience items based on duration (red if less than 1 year, green if 2y+, etc)
// - Strike-through experience items containing certain keywords
// - Record profiles already seen in search results and hide them in subsequent searches (to enable, set config variable to true below)
//
// If you make improvements, feel free to reach out and I can merge them. <aurelien.gasser at gmail.com>
// The is quick and dirty, please don't judge me 😅
// Configuration
let currentYear = 2023;
let toStrike = ['owner', 'manager', 'support'];
let hideProfilesAlreadySeen = false; // set to true to enable feature
let re = /.*<time>([0-9]+)<\/time> – <time>([0-9]+)<\/time>.*/g;
// Useful for debugging
try {
clearInterval(myInterval)
} catch (e) {}
function getMatches(string, regex, index) {
index || (index = 1); // default to the first capturing group
var matches = [];
var match;
while (match = regex.exec(string)) {
matches.push(match[index]);
}
return matches;
}
function colorLi($li, color) {
$li.attr('style', $li.attr('style') + `;color: ${color} !important`);
$li[0].children[2].setAttribute('style', `color: ${color} !important`)
}
function strikeLi($li) {
$li.attr('style', $li.attr('style') + `;text-decoration: line-through;`);
}
function addToSeen(profileNames) {
let seen = localStorage.seen || '{}'
seen = JSON.parse(seen);
for (let name of profileNames) {
seen[name] = 1;
}
localStorage.seen = JSON.stringify(seen);
}
// Run on interval because LinkedIn continuously loads profiles as you scroll down
const myInterval = setInterval(function() {
// Record profiles displayed on page
// Remove profiles previously seen
if (hideProfilesAlreadySeen) {
let seen = localStorage.seen || '{}'
seen = JSON.parse(seen);
let profileNames = mtjQuery('[data-test-link-to-profile-link]:not([data-seen])').map(function(_, el) {
const candidateName = el.innerHTML.trim()
if (seen[candidateName]) {
mtjQuery(el).closest('li').remove()
} else {
mtjQuery(el).attr('data-seen', 1);
}
return candidateName
}).toArray();
addToSeen(profileNames)
}
// Auto-Expand list of experience items
let clickable = mtjQuery('[data-test-expandable-list-button][aria-expanded="false"');
if (clickable[0]) {
clickable[0].click();
clickable[0].remove();
}
mtjQuery('[data-test-description-description]').each(function(_, li) {
let html = li.innerHTML.replace('Present', `<time>${currentYear}</time>`);
let jobTitle = li.children[0].innerHTML
const idx = jobTitle.indexOf(" at ")
let $li = mtjQuery(li)
// Strike-through experience items with target keywords
if (idx != -1) {
jobTitle = jobTitle.toLowerCase().substr(0, idx);
for (ts of toStrike) {
if (jobTitle.indexOf(ts) != -1) {
strikeLi($li);
}
}
}
// Color experience items based on duration
let y1 = parseInt(getMatches(html, re, 1)[0]);
let y2 = parseInt(getMatches(html, re, 2)[0]);
if (!y2) {
return;
}
if (y1 == y2) {
colorLi($li, '#FF4136'); // red
}
if (y2 - y1 == 1) {
colorLi($li, '#FF851B'); // orange
}
if (y2 - y1 >= 2) {
colorLi($li, '#3D9970'); // green
}
if (y2 - y1 >= 4) {
colorLi($li, 'darkgreen'); // dark green
}
});
}, 500)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment