Raw Source
KhorAMus / TooltipRemover

// ==UserScript==
// @name         TooltipRemover
// @namespace    http://tampermonkey.net/
// @version      2024-01-17
// @description  Removing all simple (title) tooltips
// @author       Albert KhorAMus Khoroshun
// @match        *://*/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license      MIT
// @updateURL https://gist.githubusercontent.com/KhorAMus/bac0bed66fb25c28127343aeb7664363/raw/27515988e1edf162852c44aaa1782000c1c3581d/TooltipRemover.js
// @downloadURL https://gist.githubusercontent.com/KhorAMus/bac0bed66fb25c28127343aeb7664363/raw/27515988e1edf162852c44aaa1782000c1c3581d/TooltipRemover.js
// ==/UserScript==

(function () {
	'use strict';

	const bodyNode = document.getElementsByTagName('body')[0];

	const config = { attributes: true, childList: true, subtree: true };

	const callback = (mutationList, observer) => {
		for (const mutation of mutationList) {
			if (mutation.attributeName === "title") {
				const node = mutation.target;
				removeTitleIfNeed(node);
			}
			else if (mutation.type === "childList") {
				var htmlElements = getHtmlElements(mutation.addedNodes);
				removeTitlesIfNeed(htmlElements);
			}
		}
	};

	const observer = new MutationObserver(callback);

	function removeTitleIfNeed(node) {
		const titleValue = node.getAttribute("title");
		if (typeof (titleValue) === "string" && titleValue.length !== 0) {
			node.setAttribute("title", "");
		}
	}

	function removeTitlesIfNeed(htmlElements) {
		for (const htmlElement of htmlElements) {
			removeTitleIfNeed(htmlElement);
			removeTitlesIfNeed(htmlElement.children);
		}
	}

	function getHtmlElements(nodes) {
		const htmlElements = [];
		for (const node of nodes) {
			if (typeof (node.tagName) === "string" && node.tagName.length !== 0) {
				htmlElements.push(node);
			}
		}
		return htmlElements;
	}

	function ready(action) {
		if (document.readyState !== 'loading') {
			action();
			return;
		}
		document.addEventListener('DOMContentLoaded', action);
	}

	ready(() => {
		removeTitlesIfNeed([bodyNode]);
		observer.observe(bodyNode, config);
	});
})();