NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Remove Unread Count from Page Title
// @description Removes unread notification counts from page titles
// @author Rami S Ejailat
// @copyright RamiEjailat (https://openuserjs.org/users/RamiEjailat)
// @updateURL https://openuserjs.org/meta/RamiEjailat/Remove_Unread_Count_from_Page_Title.meta.js
// @downloadURL https://openuserjs.org/install/RamiEjailat/Remove_Unread_Count_from_Page_Title.user.js
// @license MIT
// @version 20260215
// @namespace http://tampermonkey.net/
// @match *://*/*
// @grant none
// @icon https://cdn-icons-png.flaticon.com/256/13927/13927871.png
// @run-at document-start
// ==/UserScript==
(() => {
'use strict';
// Regular expression that matches a leading "(digits)" possibly surrounded by whitespace.
const unreadRegex = /^\s*\(\d+\)\s*/;
// Clean the document.title by removing the unread count.
function cleanTitle() {
const original = document.title;
// Exit if there is no '(' or ')'
if (!original.includes('(') || !original.includes(')')) return false;
const cleaned = original.replace(unreadRegex, '').trim();
if (cleaned !== original) {
document.title = cleaned;
return true;
}
return false;
}
// Set up a MutationObserver that watches the <title> element
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
if (
// The <title> element itself changed its text
(m.type === 'childList' && m.target.nodeName === 'TITLE') ||
// An attribute on the <title> node changed (unlikely)
(m.type === 'attributes' && m.target.nodeName === 'TITLE')
) {
cleanTitle();
break; // No need to check further mutations in this batch.
}
}
});
// Observe the current <title> element and watch for any future insertion of a <title>
function startObserving() {
const titleEl = document.querySelector('title');
if (titleEl) {
observer.observe(titleEl, {
childList: true, // textContent changes
characterData: true,
subtree: true,
attributes: true, // in case the site swaps the whole node
attributeFilter: ['text'] // just to be explicit
});
}
// In case the <title> element is added later (some SPA frameworks)
observer.observe(document.documentElement, {
childList: true,
subtree: true
// We only care about nodes being added/removed; filter inside callback.
});
}
// Run once immediately and start the observer
cleanTitle();
startObserving();
// Clean the title when the page becomes visible again (the title may get updated while the tab was not showing)
document.addEventListener('visibilitychange', () => {
if (!document.hidden) cleanTitle();
});
})();