← Index
Guide · Misc

Cleaning LinkedIn Mailboxes

Managing messages in LinkedIn mailboxes is a mess. Here is a messy way to delete the mess.

As a reluctant and infrequent user of LinkedIn, I am faced with a yearly WTF moment whenever I check my LinkedIn mailbox. Yes, this message finds me mostly well. If only LinkedIn mailboxes allowed me to select all or range of messages to delete such messages! Seems obvious, but invariably this lack of obvious “Select All” functionality makes me madder than a one-legged man in an ass-kicking contest.

Fortunately, Developer Tools allow us to open the hood and point at things and whack them with a hammer: A little poking around reveals that the mailbox functionality is implemented with Ember, so simple input.checked = true won’t do. We have to pretend to actually click an element, in this case a <label> for the Ember’s handler to fire. Trusted document.querySelectorAll could help us find the appropriate labels which we could then click, right?

document.querySelectorAll('.msg-selectable-entity__checkbox-label')
  .forEach(cb => { if (!cb.checked) cb.click(); });

Almost, but you may have some messages selected already, so, if you run the snippet above, those messages will be now deselected.

Fortunately, we can look filter checkboxes’ state filtering aria-label:

document.querySelectorAll('.msg-selectable-entity__checkbox-label')
  .filter(l => l.getAttribute('aria-label')?.startsWith('Select '))
  .forEach(l => l.click());

If you are, like me, with 10 years or so of spam, the messages are not all displayed at the same time. The internal data is paginated, and the message subject/author sets are fetched in tranches and displayed as you scroll along. You can try the following to display all the messages, but I think in newer versions of LinkedIn, the code now recycles nodes on scroll. So, in the end, instead of trying to get all the message displayed, you may need to work in batches. YMMV.

const list = document.querySelector('.msg-conversations-container__conversations-list');
let last = 0;
const scroller = setInterval(() => {
  list.scrollTop = list.scrollHeight;
  if (list.scrollHeight === last) clearInterval(scroller);
    last = list.scrollHeight;
}, 1500);

So, there you have it. A messy way dealing with messy UX choices.